typing
--- 支援型別提示¶
在 3.5 版新加入.
原始碼:Lib/typing.py
備註
Python runtime 不強制要求函式與變數的型別註釋。他們可以被第三方工具使用,如:型別檢查器、IDE、linter 等。
此模組提供 runtime 型別提示支援。
考虑下面的函数:
def moon_weight(earth_weight: float) -> str:
return f'On the moon, you would weigh {earth_weight * 0.166} kilograms.'
函数 moon_weight
接受一个预期为 float
实例的参数,如 类型提示 earth_weight: float
所指明的。 该函数预期返回一个 str
实例,如 -> str
提示所指明的。
类型提示可以是简单的类比如 float
或 str
,它们也可以更为复杂。 typing
模块提供了一套用于更高级类型提示的词汇。
新功能會頻繁的新增至 typing
模組中。typing_extensions 套件為這些新功能提供了 backport(向後移植的)版本,提供給舊版本的 Python 使用。
也參考
- "型別小抄 (Typing cheat sheet)"
型別提示的快速預覽(發布於 mypy 的文件中)
- mypy 文件的 "型別系統參考資料 (Type System Reference)" 章節
Python 的加註型別系統是基於 PEPs 進行標準化,所以這個參照 (reference) 應該在多數 Python 型別檢查器中廣為使用。(某些部分依然是特定給 mypy 使用。)
- "Python 的靜態型別 (Static Typing)"
由社群編寫的跨平台型別檢查器文件 (type-checker-agnostic) 詳細描述加註型別系統的功能、實用的加註型別衍伸工具、以及加註型別的最佳實踐 (best practice)。
有关 Python 类型系统的规范说明¶
Python 类型系统最新的规范说明可以在 "Specification for the Python type system" 查看。
型別別名¶
一個型別別名被定義來使用 type
陳述式,其建立了 TypeAliasType
的實例。在這個範例中,Vector
及 list[float]
會被當作和靜態型別檢查器一樣同等對待:
type Vector = list[float]
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
# passes type checking; a list of floats qualifies as a Vector.
new_vector = scale(2.0, [1.0, -4.2, 5.4])
型別別名對於簡化複雜的型別簽名 (complex type signature) 非常好用。舉例來說:
from collections.abc import Sequence
type ConnectionOptions = dict[str, str]
type Address = tuple[str, int]
type Server = tuple[Address, ConnectionOptions]
def broadcast_message(message: str, servers: Sequence[Server]) -> None:
...
# The static type checker will treat the previous type signature as
# being exactly equivalent to this one.
def broadcast_message(
message: str,
servers: Sequence[tuple[tuple[str, int], dict[str, str]]]) -> None:
...
type
陳述式是 Python 3.12 的新功能。為了向後相容性,型別別名可以透過簡單的賦值來建立:
Vector = list[float]
或是用 TypeAlias
標記,讓它明確的表示這是一個型別別名,而非一般的變數賦值:
from typing import TypeAlias
Vector: TypeAlias = list[float]
NewType¶
使用 NewType
輔助工具 (helper) 建立獨特型別:
from typing import NewType
UserId = NewType('UserId', int)
some_id = UserId(524313)
若它是原本型別的子類別,靜態型別檢查器會將其視為一個新的型別。這對於幫助擷取邏輯性錯誤非常有用:
def get_user_name(user_id: UserId) -> str:
...
# passes type checking
user_a = get_user_name(UserId(42351))
# fails type checking; an int is not a UserId
user_b = get_user_name(-1)
你依然可以在對於型別 UserId
的變數中執行所有 int
的操作。這讓你可以在預期接受 int
的地方傳遞一個 UserId
,還能預防你意外使用無效的方法建立一個 UserId
:
# 'output' is of type 'int', not 'UserId'
output = UserId(23413) + UserId(54341)
注意這只會透過靜態型別檢查器強制檢查。在 runtime 中,陳述式 (statement) Derived = NewType('Derived', Base)
會使 Derived
成為一個 callable(可呼叫物件),會立即回傳任何你傳遞的引數。這意味著 expression (運算式)Derived(some_value)
不會建立一個新的類別或過度引入原有的函式呼叫。
更精確地說,expression some_value is Derived(some_value)
在 runtime 永遠為 true。
這會無法建立一個 Derived
的子型別:
from typing import NewType
UserId = NewType('UserId', int)
# Fails at runtime and does not pass type checking
class AdminUserId(UserId): pass
無論如何,這有辦法基於 '衍生的' NewType
建立一個 NewType
:
from typing import NewType
UserId = NewType('UserId', int)
ProUserId = NewType('ProUserId', UserId)
以及針對 ProUserId
的型別檢查會如期運作。
更多細節請見 PEP 484。
備註
請記得使用型別別名是宣告兩種型別是互相相等的。使用 type Alias = Original
則會讓靜態型別檢查器在任何情況之下將 Alias
視為與 Original
完全相等。這當你想把複雜的型別簽名進行簡化時,非常好用。
相反的,NewType
宣告一個型別會是另外一種型別的子類別。使用 Derived = NewType('Derived', Original)
會使靜態型別檢查器將 Derived
視為 Original
的子類別,也意味著一個型別為 Original
的值,不能被使用在任何預期接收到型別 Derived
的值的區域。這當你想用最小的 runtime 成本預防邏輯性錯誤而言,非常有用。
在 3.5.2 版新加入.
在 3.10 版的變更: 現在的 NewType
比起一個函式更像一個類別。因此,比起一般的函式,呼叫 NewType
需要額外的 runtime 成本。
在 3.11 版的變更: 呼叫 NewType
的效能已經恢復與 Python 3.9 相同的水準。
註釋 callable 物件¶
函式,或者是其他 callable 物件,可以使用 collections.abc.Callable
或 typing.Callable
進行註釋。 Callable[[int], str]
象徵為一個函式,可以接受一個型別為 int
的引數,並回傳一個 str
。
舉例來說:
from collections.abc import Callable, Awaitable
def feeder(get_next_item: Callable[[], str]) -> None:
... # Body
def async_query(on_success: Callable[[int], None],
on_error: Callable[[int, Exception], None]) -> None:
... # Body
async def on_update(value: str) -> None:
... # Body
callback: Callable[[str], Awaitable[None]] = on_update
使用下標語法 (subscription syntax) 時,必須使用到兩個值,分別為引述串列以及回傳類別。引數串列必須為一個型別串列:ParamSpec
、Concatenate
或是一個刪節號 (ellipsis)。回傳類別必為一個單一類別。
若刪節號文字 ...
被當作引數串列給定,其指出一個具任何、任意參數列表的 callable 會被接受:
def concat(x: str, y: str) -> str:
return x + y
x: Callable[..., str]
x = str # OK
x = concat # Also OK
Callable
不如有可變數量引數的函式、overloaded functions
、或是僅限關鍵字參數的函式,可以表示複雜簽名。然而,這些簽名可以透過定義一個具有 __call__()
方法的 Protocol
類別進行表示:
from collections.abc import Iterable
from typing import Protocol
class Combiner(Protocol):
def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ...
def batch_proc(data: Iterable[bytes], cb_results: Combiner) -> bytes:
for item in data:
...
def good_cb(*vals: bytes, maxlen: int | None = None) -> list[bytes]:
...
def bad_cb(*vals: bytes, maxitems: int | None) -> list[bytes]:
...
batch_proc([], good_cb) # OK
batch_proc([], bad_cb) # Error! Argument 2 has incompatible type because of
# different name and kind in the callback
Callable 物件可以取用其他 callable 當作引數使用,可以透過 ParamSpec
指出他們的參數型別是個別獨立的。另外,如果這個 callable 從其他 callable 新增或刪除引數時,將會使用到 Concatenate
運算子。他們可以分別採用 Callable[ParamSpecVariable, ReturnType]
以及 Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType]
的形式。
在 3.10 版的變更: Callable
現已支援 ParamSpec
以及 Concatenate
。請參閱 PEP 612 閱讀詳細內容。
也參考
ParamSpec
以及 Concatenate
的文件中,提供範例如何在 Callable
中使用。
泛型¶
因為關於物件的型別資訊留存在容器之內,且無法使用通用的方式進行靜態推論 (statically inferred),許多標準函式庫的容器類別支援以下標來表示容器內預期的元素。
from collections.abc import Mapping, Sequence
class Employee: ...
# Sequence[Employee] indicates that all elements in the sequence
# must be instances of "Employee".
# Mapping[str, str] indicates that all keys and all values in the mapping
# must be strings.
def notify_by_email(employees: Sequence[Employee],
overrides: Mapping[str, str]) -> None: ...
泛型函式及類別可以使用型別參數語法 (type parameter syntax) 進行參數化 (parameterize) :
from collections.abc import Sequence
def first[T](l: Sequence[T]) -> T: # Function is generic over the TypeVar "T"
return l[0]
或是直接使用 TypeVar
工廠 (factory):
from collections.abc import Sequence
from typing import TypeVar
U = TypeVar('U') # Declare type variable "U"
def second(l: Sequence[U]) -> U: # Function is generic over the TypeVar "U"
return l[1]
在 3.12 版的變更: 在 Python 3.12 中,泛型的語法支援是全新功能。
註釋元組 (tuple)¶
在 Python 大多數的容器當中,加註型別系統認為容器內的所有元素會是相同型別。舉例來說:
from collections.abc import Mapping
# Type checker will infer that all elements in ``x`` are meant to be ints
x: list[int] = []
# Type checker error: ``list`` only accepts a single type argument:
y: list[int, str] = [1, 'foo']
# Type checker will infer that all keys in ``z`` are meant to be strings,
# and that all values in ``z`` are meant to be either strings or ints
z: Mapping[str, str | int] = {}
list
只接受一個型別引數,所以型別檢查器可能在上述 y
賦值 (assignment) 觸發錯誤。類似的範例,Mapping
只接受兩個型別引數:第一個引數指出 keys(鍵)的型別;第二個引數指出 values(值)的型別。
然而,與其他多數的 Python 容器不同,在慣用的 (idiomatic) Python 程式碼中,元組可以擁有不完全相同型別的元素是相當常見的。為此,元組在 Python 的加註型別系統中是個特例 (special-cased)。tuple
接受任何數量的型別引數:
# OK: ``x`` is assigned to a tuple of length 1 where the sole element is an int
x: tuple[int] = (5,)
# OK: ``y`` is assigned to a tuple of length 2;
# element 1 is an int, element 2 is a str
y: tuple[int, str] = (5, "foo")
# Error: the type annotation indicates a tuple of length 1,
# but ``z`` has been assigned to a tuple of length 3
z: tuple[int] = (1, 2, 3)
為了標示一個元組可以為任意長度,且所有元素皆是相同型別 T
,請使用 tuple[T, ...]
進行標示。為了標示一個空元組,請使用 tuple[()]
。單純使用 tuple
作為註釋,會與使用 tuple[Any, ...]
是相等的:
x: tuple[int, ...] = (1, 2)
# These reassignments are OK: ``tuple[int, ...]`` indicates x can be of any length
x = (1, 2, 3)
x = ()
# This reassignment is an error: all elements in ``x`` must be ints
x = ("foo", "bar")
# ``y`` can only ever be assigned to an empty tuple
y: tuple[()] = ()
z: tuple = ("foo", "bar")
# These reassignments are OK: plain ``tuple`` is equivalent to ``tuple[Any, ...]``
z = (1, 2, 3)
z = ()
類別物件的型別¶
一個變數被註釋為 C
可以接受一個型別為 C
的值。相對的,一個變數備註解為 type[C]
(或 typing.Type[C]
)可以接受本身為該類別的值 -- 具體來說,他可能會接受 C
的類別物件。舉例來說:
a = 3 # Has type ``int``
b = int # Has type ``type[int]``
c = type(a) # Also has type ``type[int]``
請記得 type[C]
是共變 (covariant) 的:
class User: ...
class ProUser(User): ...
class TeamUser(User): ...
def make_new_user(user_class: type[User]) -> User:
# ...
return user_class()
make_new_user(User) # OK
make_new_user(ProUser) # Also OK: ``type[ProUser]`` is a subtype of ``type[User]``
make_new_user(TeamUser) # Still fine
make_new_user(User()) # Error: expected ``type[User]`` but got ``User``
make_new_user(int) # Error: ``type[int]`` is not a subtype of ``type[User]``
type
僅有的合法參數是類別、Any
、型別變數以及這些型別任意組合成的聯集。舉例來說:
def new_non_team_user(user_class: type[BasicUser | ProUser]): ...
new_non_team_user(BasicUser) # OK
new_non_team_user(ProUser) # OK
new_non_team_user(TeamUser) # Error: ``type[TeamUser]`` is not a subtype
# of ``type[BasicUser | ProUser]``
new_non_team_user(User) # Also an error
type[Any]
等價於 type
,其為 Python metaclass 階層結構 (hierachy)。
使用者定義泛型型別¶
一個使用者定義的類別可以被定義成一個泛型類別。
from logging import Logger
class LoggedVar[T]:
def __init__(self, value: T, name: str, logger: Logger) -> None:
self.name = name
self.logger = logger
self.value = value
def set(self, new: T) -> None:
self.log('Set ' + repr(self.value))
self.value = new
def get(self) -> T:
self.log('Get ' + repr(self.value))
return self.value
def log(self, message: str) -> None:
self.logger.info('%s: %s', self.name, message)
這個語法指出類別 LoggedVar
透過一個單一的 型別變數 T
進行參數化 (parameterised)。這使得 T
在類別中有效的成為型別。
泛型類別隱性繼承了 Generic
。為了相容 Python 3.11 及更早版本,也可以明確的繼承 Generic
並指出是一個泛型類別:
from typing import TypeVar, Generic
T = TypeVar('T')
class LoggedVar(Generic[T]):
...
泛型類別有 __class_getitem__()
方法,其意味著可以在 runtime 進行參數化(如下述的 LoggedVar[int]
):
from collections.abc import Iterable
def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
for var in vars:
var.set(0)
一個泛型型別可以有任意數量的型別變數。所有種類的 TypeVar
都可以作為泛型型別的參數:
from typing import TypeVar, Generic, Sequence
class WeirdTrio[T, B: Sequence[bytes], S: (int, str)]:
...
OldT = TypeVar('OldT', contravariant=True)
OldB = TypeVar('OldB', bound=Sequence[bytes], covariant=True)
OldS = TypeVar('OldS', int, str)
class OldWeirdTrio(Generic[OldT, OldB, OldS]):
...
Generic
的每個型別變數引數必不相同。因此以下是無效的:
from typing import TypeVar, Generic
...
class Pair[M, M]: # SyntaxError
...
T = TypeVar('T')
class Pair(Generic[T, T]): # INVALID
...
泛型類別亦可以繼承其他類別:
from collections.abc import Sized
class LinkedList[T](Sized):
...
當繼承泛型類別時,部份的型別參數可固定:
from collections.abc import Mapping
class MyDict[T](Mapping[str, T]):
...
在這種情況下 MyDict
有一個單一的參數 T
。
若使用泛型類別卻沒有特指型別參數,則會將每個位置視為 Any
。在下列的範例中 MyIterable
不是泛型,但隱性繼承了 Iterable[Any]
:
from collections.abc import Iterable
class MyIterable(Iterable): # Same as Iterable[Any]
...
使用者定義的泛型型別別名也有支援。例如:
from collections.abc import Iterable
type Response[S] = Iterable[S] | int
# Return type here is same as Iterable[str] | int
def response(query: str) -> Response[str]:
...
type Vec[T] = Iterable[tuple[T, T]]
def inproduct[T: (int, float, complex)](v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]]
return sum(x*y for x, y in v)
為了向後相容性,泛型型別別名可以透過簡單的賦值來建立:
from collections.abc import Iterable
from typing import TypeVar
S = TypeVar("S")
Response = Iterable[S] | int
在 3.7 版的變更: Generic
不再是一個自訂的 metaclass。
在 3.12 版的變更: 在版本 3.12 新增了泛型及型別別名的語法支援。在之前的版本中,泛型類別必須顯性繼承 Generic
或是包含一個型別變數在基底類別 (base) 當中。
使用者定義的參數運算式 (parameter expression) 泛型一樣有支援,透過 [**P]
格式的參數規格變數來進行表示。對於上述作為參數規格變數的型別變數,將持續被型別模組視為一個特定的型別變數。對此,其中一個例外是一個型別列表可以替代 ParamSpec
:
>>> class Z[T, **P]: ... # T is a TypeVar; P is a ParamSpec
...
>>> Z[int, [dict, float]]
__main__.Z[int, [dict, float]]
具有 ParamSpec
的泛型類別可以透過顯性繼承 Generic
進行建立。在這種情況下,不需要使用 **
:
from typing import ParamSpec, Generic
P = ParamSpec('P')
class Z(Generic[P]):
...
另外一個 TypeVar
以及 ParamSpec
之間的差異是,基於美觀因素,只有一個參數規格變數的泛型可以接受如 X[[Type1, Type2, ...]]
以及 X[Type1, Type2, ...]
的參數列表。在內部中,後者會被轉換為前者,所以在下方的範例中為相等的:
>>> class X[**P]: ...
...
>>> X[int, str]
__main__.X[[int, str]]
>>> X[[int, str]]
__main__.X[[int, str]]
請記得,具有 ParamSpec
的泛型在某些情況下替換之後可能不會有正確的 __parameters__
,因為參數規格主要還是用於靜態型別檢查。
一個使用者定義的泛型類別可以將 ABC 作為他們的基底類別,且不會有 metaclass 衝突。泛型的 metaclass 則不支援。參數化泛型的輸出將被存為快取,而在型別模組中多數的型別皆為 hashable 且可以比較相等性。
Any
型別¶
Any
是一種特別的型別。一個靜態型別檢查器會將每個型別視為可相容於 Any
且 Any
也可以相容於每個型別。
這意味著如果在一個為 Any
的值上執行任何操作或呼叫方法是可行的,且可以賦值給任意變數:
from typing import Any
a: Any = None
a = [] # OK
a = 2 # OK
s: str = ''
s = a # OK
def foo(item: Any) -> int:
# Passes type checking; 'item' could be any type,
# and that type might have a 'bar' method
item.bar()
...
請注意,當賦予型別為 Any
的值更精確的型別時,將不會執行任何型別檢查。舉例來說,靜態型別檢查器不會在 runtime 中,將 a
賦值給 s
的情況下回報錯誤,儘管 s
是被宣告為型別 str
卻接收到 int
的值!
另外,所有缺少回傳型別或參數型別的函式將會隱性預設為 Any
:
def legacy_parser(text):
...
return data
# A static type checker will treat the above
# as having the same signature as:
def legacy_parser(text: Any) -> Any:
...
return data
當你需要混和動態及靜態的型別程式碼,這個行為允許 Any
被當作一個緊急出口 (escape hatch)使用。
Any
的行為對比 object
的行為。與 Any
相似,所有的型別會作為 object
的子型別。然而,不像 Any
,反之不亦然:object
並不是一個其他型別的子型別。
這意味著當一個值的型別為 object
時,型別檢查器會拒絕幾乎所有的操作,並將賦予這個值到一個特定型別變數(或是當作回傳值使用)視為一個型別錯誤。舉例來說:
def hash_a(item: object) -> int:
# Fails type checking; an object does not have a 'magic' method.
item.magic()
...
def hash_b(item: Any) -> int:
# Passes type checking
item.magic()
...
# Passes type checking, since ints and strs are subclasses of object
hash_a(42)
hash_a("foo")
# Passes type checking, since Any is compatible with all types
hash_b(42)
hash_b("foo")
使用 object
,將指出在型別安全 (typesafe) 的習慣之下一個值可以為任意型別。使用 Any
,將指出這個值是個動態型別。
標稱 (nominal) 子型別 vs 結構子型別¶
最初 PEP 484 定義 Python 靜態型別系統使用標稱子型別。這意味著只有 A
為 B
的子類別時,A
才被允許使用在預期是類別 B
出現的地方。
這個需求之前也被運用在抽象基底類別,例如 Iterable
。這種方式的問題在於,一個類別需要顯式的標記來支援他們,這並不符合 Python 風格,也不像一個常見的慣用動態型別 Python 程式碼。舉例來說,下列程式碼符合 PEP 484:
from collections.abc import Sized, Iterable, Iterator
class Bucket(Sized, Iterable[int]):
...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[int]: ...
PEP 544 可以透過使用上方的程式碼,且在類別定義時不用顯式基底類別解決這個問題,讓 Bucket
被靜態型別檢查器隱性認為是 Sized
以及 Iterable[int]
兩者的子型別。這就是眾所周知的結構子型別(或是靜態鴨子型別):
from collections.abc import Iterator, Iterable
class Bucket: # Note: no base classes
...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[int]: ...
def collect(items: Iterable[int]) -> int: ...
result = collect(Bucket()) # Passes type check
而且,基於一個特別的型別 Protocol
建立子型別時,使用者可以定義新的協定並充份發揮結構子型別的優勢(請見下方範例)。
模組內容¶
模組 typing
定義了下列的類別、函式以及裝飾器。
特別型別原語 (primitive)¶
特別型別¶
這些可以在註釋中做為型別。他們並不支援 []
的下標使用。
- typing.Any¶
特別型別,指出一個不受約束 (unconstrained) 的型別。
在 3.11 版的變更:
Any
可以作為一個基礎類別。這對於在任何地方使用鴨子型別或是高度動態的型別,避免型別檢查器的錯誤是非常有用的。
- typing.AnyStr¶
一個不受約束的型別變數。
定義:
AnyStr = TypeVar('AnyStr', str, bytes)
AnyStr
是對於函式有用的,他可以接受str
或bytes
引數但不可以將此兩種混合。舉例來說:
def concat(a: AnyStr, b: AnyStr) -> AnyStr: return a + b concat("foo", "bar") # OK, output has type 'str' concat(b"foo", b"bar") # OK, output has type 'bytes' concat("foo", b"bar") # Error, cannot mix str and bytes
請注意,儘管他的名稱相近,
AnyStr
與Any
型別無關,更不代表是「任何字串」的意思。尤其,AnyStr
與str | bytes
兩者不同且具有不同的使用情境:# Invalid use of AnyStr: # The type variable is used only once in the function signature, # so cannot be "solved" by the type checker def greet_bad(cond: bool) -> AnyStr: return "hi there!" if cond else b"greetings!" # The better way of annotating this function: def greet_proper(cond: bool) -> str | bytes: return "hi there!" if cond else b"greetings!"
- typing.LiteralString¶
特別型別,只包含文本字串。
任何文本字串都相容於
LiteralString
,對於另一個LiteralString
亦是如此。然而,若是一個型別僅為str
的物件則不相容。一個字串若是透過組合多個LiteralString
型別的物件建立,則此字串也可以視為LiteralString
。舉例來說:
def run_query(sql: LiteralString) -> None: ... def caller(arbitrary_string: str, literal_string: LiteralString) -> None: run_query("SELECT * FROM students") # OK run_query(literal_string) # OK run_query("SELECT * FROM " + literal_string) # OK run_query(arbitrary_string) # type checker error run_query( # type checker error f"SELECT * FROM students WHERE name = {arbitrary_string}" )
LiteralString
對於敏感的 API 來說是有用的,其中任意的使用者產生的字串可能會產生問題。舉例來說,上面兩個案例中產生的型別檢查器錯誤是脆弱的且容易受到 SQL 注入攻擊。更多細節請見 PEP 675。
在 3.11 版新加入.
- typing.Never¶
底部型別 (bottom type),為一個型別但沒有任何的成員。
這可以被用來定義一個不應被呼叫的函式,或是一個不會回傳的函式:
from typing import Never def never_call_me(arg: Never) -> None: pass def int_or_str(arg: int | str) -> None: never_call_me(arg) # type checker error match arg: case int(): print("It's an int") case str(): print("It's a str") case _: never_call_me(arg) # OK, arg is of type Never
在 3.11 版新加入: 在舊的 Python 版本當中,
NoReturn
可以用來當作一樣的概念使用。新增Never
之後,則讓這個含義變得更為明確。
- typing.NoReturn¶
特別型別,指出一個永不回傳的函式。
舉例來說:
from typing import NoReturn def stop() -> NoReturn: raise RuntimeError('no way')
NoReturn
可以用來作為一個底部型別,一個沒有值的型別。從 Python 3.11 開始,型別Never
應該改用這個概念。型別檢查器應該將這兩種型別視為相等的。在 3.6.2 版新加入.
- typing.Self¶
特別型別,用來表示當前類別之內 (enclosed class)。
舉例來說:
from typing import Self, reveal_type class Foo: def return_self(self) -> Self: ... return self class SubclassOfFoo(Foo): pass reveal_type(Foo().return_self()) # Revealed type is "Foo" reveal_type(SubclassOfFoo().return_self()) # Revealed type is "SubclassOfFoo"
這個註釋在語意上相等於下列內容,且形式更為簡潔:
from typing import TypeVar Self = TypeVar("Self", bound="Foo") class Foo: def return_self(self: Self) -> Self: ... return self
一般來說,如果某個東西回傳
self
如上方的範例所示,你則應該使用Self
做為回傳值的註釋。若Foo.return_self
被註釋為回傳"Foo"
,則型別檢查器應該推論這個從SubclassOfFoo.return_self
回傳的物件為Foo
型別,而並非回傳SubclassOfFoo
型別。其他常見的使用案例包含:
classmethod
被用來作為替代的建構函式 (constructor) 並回傳cls
參數的實例。註釋一個回傳自己的
__enter__()
方法。
當類別被子類別化時,若方法不保證回傳一個子類別的實例,你不應該使用
Self
作為回傳註釋:class Eggs: # Self would be an incorrect return annotation here, # as the object returned is always an instance of Eggs, # even in subclasses def returns_eggs(self) -> "Eggs": return Eggs()
更多細節請見 PEP 673。
在 3.11 版新加入.
- typing.TypeAlias¶
做為明確宣告一個型別別名 的特別註釋。
舉例來說:
from typing import TypeAlias Factors: TypeAlias = list[int]
TypeAlias
在舊的 Python 版本中特別有用,其註釋別名可以用來進行傳遞參照 (forward reference),因為對於型別檢查器來說,分辨這些別名與一般的變數賦值相當困難:from typing import Generic, TypeAlias, TypeVar T = TypeVar("T") # "Box" does not exist yet, # so we have to use quotes for the forward reference on Python <3.12. # Using ``TypeAlias`` tells the type checker that this is a type alias declaration, # not a variable assignment to a string. BoxOfStrings: TypeAlias = "Box[str]" class Box(Generic[T]): @classmethod def make_box_of_strings(cls) -> BoxOfStrings: ...
更多細節請見 PEP 613。
在 3.10 版新加入.
在 3.12 版之後被棄用:
TypeAlias
被棄用,請改用type
陳述式來建立TypeAliasType
的實例,其自然可以支援傳遞參照的使用。請注意,雖然TypeAlias
以及TypeAliasType
提供相似的用途且具有相似的名稱,他們是不同的,且後者不是前者的型別。現在還沒有移除TypeAlias
的計畫,但鼓勵使用者們遷移 (migrate) 至type
陳述式。
特殊形式¶
这些内容在注解中可以视为类型,且都支持下标用法([]
),但每个都有唯一的语法。
- typing.Union¶
联合类型;
Union[X, Y]
等价于X | Y
,意味着满足 X 或 Y 之一。要定义一个联合类型,可以使用类似
Union[int, str]
或简写int | str
。建议使用这种简写。细节:参数必须是某种类型,且至少有一个。
联合类型之联合类型会被展平,例如:
Union[Union[int, str], float] == Union[int, str, float]
单参数之联合类型就是该参数自身,例如:
Union[int] == int # The constructor actually returns int
冗余的参数会被跳过,例如:
Union[int, str, int] == Union[int, str] == int | str
比较联合类型,不涉及参数顺序,例如:
Union[int, str] == Union[str, int]
不可创建
Union
的子类或实例。你不能寫成
Union[X][Y]
。
在 3.7 版的變更: 在运行时,不要移除联合类型中的显式子类。
在 3.10 版的變更: 联合类型现在可以写成
X | Y
。 参见 联合类型表达式。
- typing.Optional¶
Optional[X]
等价于X | None
(或Union[X, None]
) 。注意,可选类型与含默认值的可选参数不同。含默认值的可选参数不需要在类型注解上添加
Optional
限定符,因为它仅是可选的。例如:def foo(arg: int = 0) -> None: ...
另一方面,显式应用
None
值时,不管该参数是否可选,Optional
都适用。例如:def foo(arg: Optional[int] = None) -> None: ...
在 3.10 版的變更: 可选参数现在可以写成
X | None
。 参见 联合类型表达式。
- typing.Concatenate¶
特殊形式,用于注解高阶函数。
Concatenate
可用于与 Callable 和ParamSpec
连用来注解高阶可调用对象,该可象可以添加、移除或转换另一个可调用对象的形参。 使用形式为Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]
。Concatenate
目前仅可用作传给 Callable 的第一个参数。传给Concatenate
的最后一个形参必须是ParamSpec
或省略号(...
)。例如,为了注释一个装饰器
with_lock
,它为被装饰的函数提供了threading.Lock
,Concatenate
可以用来表示with_lock
期望一个可调用对象,该对象接收一个Lock
作为第一个参数,并返回一个具有不同类型签名的可调用对象。 在这种情况下,ParamSpec
表示返回的可调用对象的参数类型取决于被传入的可调用程序的参数类型:from collections.abc import Callable from threading import Lock from typing import Concatenate # Use this lock to ensure that only one thread is executing a function # at any time. my_lock = Lock() def with_lock[**P, R](f: Callable[Concatenate[Lock, P], R]) -> Callable[P, R]: '''A type-safe decorator which provides a lock.''' def inner(*args: P.args, **kwargs: P.kwargs) -> R: # Provide the lock as the first argument. return f(my_lock, *args, **kwargs) return inner @with_lock def sum_threadsafe(lock: Lock, numbers: list[float]) -> float: '''Add a list of numbers together in a thread-safe manner.''' with lock: return sum(numbers) # We don't need to pass in the lock ourselves thanks to the decorator. sum_threadsafe([1.1, 2.2, 3.3])
在 3.10 版新加入.
也參考
PEP 612 -- 参数规范变量(引入
ParamSpec
和Concatenate
的 PEP)
- typing.Literal¶
特殊类型注解形式,用于定义“字面值类型”。
Literal
可以用来向类型检查器说明被注解的对象具有与所提供的字面量之一相同的值。舉例來說:
def validate_simple(data: Any) -> Literal[True]: # always returns True ... type Mode = Literal['r', 'rb', 'w', 'wb'] def open_helper(file: str, mode: Mode) -> str: ... open_helper('/some/path', 'r') # Passes type check open_helper('/other/path', 'typo') # Error in type checker
Literal[...]
不能创建子类。在运行时,任意值均可作为Literal[...]
的类型参数,但类型检查器可以对此加以限制。字面量类型详见 PEP 586 。在 3.8 版新加入.
- typing.ClassVar¶
特殊类型注解构造,用于标注类变量。
如 PEP 526 所述,打包在 ClassVar 内的变量注解是指,给定属性应当用作类变量,而不应设置在类实例上。用法如下:
class Starship: stats: ClassVar[dict[str, int]] = {} # class variable damage: int = 10 # instance variable
ClassVar
仅接受类型,也不能使用下标。ClassVar
本身不是类,不应用于isinstance()
或issubclass()
。ClassVar
不改变 Python 运行时行为,但可以用于第三方类型检查器。例如,类型检查器会认为以下代码有错:enterprise_d = Starship(3000) enterprise_d.stats = {} # Error, setting class variable on instance Starship.stats = {} # This is OK
在 3.5.3 版新加入.
- typing.Final¶
特殊类型注解构造,用于向类型检查器表示最终名称。
不能在任何作用域中重新分配最终名称。类作用域中声明的最终名称不能在子类中重写。
舉例來說:
MAX_SIZE: Final = 9000 MAX_SIZE += 1 # Error reported by type checker class Connection: TIMEOUT: Final[int] = 10 class FastConnector(Connection): TIMEOUT = 1 # Error reported by type checker
这些属性没有运行时检查。详见 PEP 591。
在 3.8 版新加入.
- typing.Required¶
特殊类型注解构造,用于标记
TypedDict
键为必填项。主要用於
total=False
的 TypedDict。更多細節請見TypedDict
與 PEP 655。在 3.11 版新加入.
- typing.Annotated¶
特殊类型注解形式,用于向注解添加特定于上下文的元数据。
使用注解
Annotated[T, x]
将元数据x
添加到给定类型T
。使用Annotated
添加的元数据可以被静态分析工具使用,也可以在运行时使用。在运行时使用的情况下,元数据存储在__metadata__
属性中。如果库或工具遇到注解
Annotated[T, x]
,并且没有针对这一元数据的特殊处理逻辑,则应该忽略该元数据,简单地将注解视为T
。因此,Annotated
对于希望将注解用于 Python 的静态类型注解系统之外的目的的代码很有用。使用
Annotated[T, x]
作为注解仍然允许对T
进行静态类型检查,因为类型检查器将简单地忽略元数据x
。因此,Annotated
不同于@no_type_check
装饰器,后者虽然也可以用于在类型注解系统范围之外添加注解,但是会完全禁用对函数或类的类型检查。具体解释元数据的方式由遇到
Annotated
注解的工具或库来负责。遇到Annotated
类型的工具或库可以扫描元数据的各个元素以确定其是否有意处理(比如使用isinstance()
)。- Annotated[<type>, <metadata>]
以下示例演示在进行区间范围分析时使用
Annotated
将元数据添加到类型注解的方法:@dataclass class ValueRange: lo: int hi: int T1 = Annotated[int, ValueRange(-10, 5)] T2 = Annotated[T1, ValueRange(-20, 3)]
语法细节:
Annotated
的第一个参数必须是有效的类型。可提供多个元数据的元素(
Annotated
支持可变参数):@dataclass class ctype: kind: str Annotated[int, ValueRange(3, 10), ctype("char")]
由处理注解的工具决定是否允许向一个注解中添加多个元数据元素,以及如何合并这些注解。
Annotated
至少要有两个参数(Annotated[int]
是无效的)元数据元素的顺序会被保留,且影响等价检查:
assert Annotated[int, ValueRange(3, 10), ctype("char")] != Annotated[ int, ctype("char"), ValueRange(3, 10) ]
嵌套的
Annotated
类型会被展平。元数据元素从最内层的注解开始依次展开:assert Annotated[Annotated[int, ValueRange(3, 10)], ctype("char")] == Annotated[ int, ValueRange(3, 10), ctype("char") ]
元数据中的重复元素不会被移除:
assert Annotated[int, ValueRange(3, 10)] != Annotated[ int, ValueRange(3, 10), ValueRange(3, 10) ]
Annotated
可以与嵌套别名和泛型别名一起使用:@dataclass class MaxLen: value: int type Vec[T] = Annotated[list[tuple[T, T]], MaxLen(10)] # When used in a type annotation, a type checker will treat "V" the same as # ``Annotated[list[tuple[int, int]], MaxLen(10)]``: type V = Vec[int]
Annotated
不能与已解包的TypeVarTuple
一起使用:type Variadic[*Ts] = Annotated[*Ts, Ann1] # NOT valid
這會等價於:
Annotated[T1, T2, T3, ..., Ann1]
其中
T1
、T2
等都是TypeVars
。这种写法无效:应当只有一个类型被传递给 Annotated。默认情况下,
get_type_hints()
会去除注解中的元数据。传入include_extras=True
可以保留元数据:>>> from typing import Annotated, get_type_hints >>> def func(x: Annotated[int, "metadata"]) -> None: pass ... >>> get_type_hints(func) {'x': <class 'int'>, 'return': <class 'NoneType'>} >>> get_type_hints(func, include_extras=True) {'x': typing.Annotated[int, 'metadata'], 'return': <class 'NoneType'>}
在运行时,与特定
Annotated
类型相关联的元数据可通过__metadata__
属性来获取:>>> from typing import Annotated >>> X = Annotated[int, "very", "important", "metadata"] >>> X typing.Annotated[int, 'very', 'important', 'metadata'] >>> X.__metadata__ ('very', 'important', 'metadata')
也參考
- PEP 593 - 灵活的函数与变量标注
该 PEP 将
Annotated
引入到标准库中。
在 3.9 版新加入.
- typing.TypeGuard¶
用于标记用户自定义类型防护函数的特殊类型构造。
TypeGuard
可被用于标注用户自定义类型保护函数的返回类型。TypeGuard
只接受单独的类型参数。 在运行时,以这种方式标记的函数应当返回一个布尔值。TypeGuard
旨在使 类型收窄 受益——这是静态类型检查器用以确定表达式在代码流中的较精确类型的一种技术。通常,类型收窄是以分析条件代码流并将收窄应用于一个代码块来完成的,涉及到的条件表达式有时即被称为“type guard”。def is_str(val: str | float): # "isinstance" type guard if isinstance(val, str): # Type of ``val`` is narrowed to ``str`` ... else: # Else, type of ``val`` is narrowed to ``float``. ...
定义一个布尔函数作为 type guard 有时会很方便,此时应使用
TypeGuard[...]
作为其返回类型以提醒静态类型检查器注意这一意图。-> TypeGuard
告诉静态类型检查器,某函数:返回一个布尔值。
如果返回值是
True
,那么其参数的类型是TypeGuard
内的类型。
舉例來說:
def is_str_list(val: list[object]) -> TypeGuard[list[str]]: '''Determines whether all objects in the list are strings''' return all(isinstance(x, str) for x in val) def func1(val: list[object]): if is_str_list(val): # Type of ``val`` is narrowed to ``list[str]``. print(" ".join(val)) else: # Type of ``val`` remains as ``list[object]``. print("Not a list of strings!")
如果
is_str_list
是一个类或实例方法,那么TypeGuard
中的类型映射到cls
或self
之后的第二个参数的类型。简而言之,
def foo(arg: TypeA) -> TypeGuard[TypeB]: ...
形式的意思是:如果foo(arg)
返回True
,那么arg
将把TypeA
缩小为TypeB
。備註
TypeB
无需为TypeA
的缩小形式 -- 它甚至可以是扩大形式。 主要原因是允许像把list[object]
缩小到list[str]
这样的事情,即使后者不是前者的一个子类型,因为list
是不变的。 编写类型安全的类型防护的责任留给了用户。TypeGuard
也适用于类型变量。 详情参见 PEP 647。在 3.10 版新加入.
- typing.Unpack¶
在概念上将对象标记为已解包的类型运算符。
例如,在一个 类型变量元组 上使用解包运算符
*
就等价于使用Unpack
来将该类型变量元组标记为已被解包:Ts = TypeVarTuple('Ts') tup: tuple[*Ts] # Effectively does: tup: tuple[Unpack[Ts]]
实际上,
Unpack
在typing.TypeVarTuple
和builtins.tuple
类型的上下文中可以和*
互换使用。 你可能会看到Unpack
在较旧版本的 Python 中被显式地使用,这时*
在特定场合则是无法使用的:# In older versions of Python, TypeVarTuple and Unpack # are located in the `typing_extensions` backports package. from typing_extensions import TypeVarTuple, Unpack Ts = TypeVarTuple('Ts') tup: tuple[*Ts] # Syntax error on Python <= 3.10! tup: tuple[Unpack[Ts]] # Semantically equivalent, and backwards-compatible
Unpack
也可以与typing.TypedDict
一起使用以便在函数签名中对**kwargs
进行类型标注:from typing import TypedDict, Unpack class Movie(TypedDict): name: str year: int # This function expects two keyword arguments - `name` of type `str` # and `year` of type `int`. def foo(**kwargs: Unpack[Movie]): ...
请参阅 PEP 692 了解将
Unpack
用于**kwargs
类型标注的更多细节。在 3.11 版新加入.
构造泛型类型与类型别名¶
下列类不应被直接用作标注。 它们的设计目标是作为创建泛型类型和类型别名的构件。
这些对象可通过特殊语法 (类型形参列表 和 type
语句) 来创建。 为了与 Python 3.11 及更早版本的兼容性,它们也可不用专门的语法来创建,如下文所述。
- class typing.Generic¶
用于泛型类型的抽象基类。
泛型类型通常是通过在类名后添加一个类型形参列表来声明的:
class Mapping[KT, VT]: def __getitem__(self, key: KT) -> VT: ... # Etc.
这样的类将隐式地继承自
Generic
。 对于该语法的运行语义的讨论参见 语言参考。该类的用法如下:
def lookup_name[X, Y](mapping: Mapping[X, Y], key: X, default: Y) -> Y: try: return mapping[key] except KeyError: return default
此处函数名之后的圆括号是表示 泛型函数。
为了保持向下兼容性,泛型类也可通过显式地继承自
Generic
来声明。 在此情况下,类型形参必须单独声明:KT = TypeVar('KT') VT = TypeVar('VT') class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.
- class typing.TypeVar(name, *constraints, bound=None, covariant=False, contravariant=False, infer_variance=False)¶
类型变量。
构造类型变量的推荐方式是使用针对 泛型函数, 泛型类 和 泛型类型别名 的专门语法:
class Sequence[T]: # T is a TypeVar ...
此语法也可被用于创建绑定和带约束的类型变量:
class StrSequence[S: str]: # S is a TypeVar bound to str ... class StrOrBytesSequence[A: (str, bytes)]: # A is a TypeVar constrained to str or bytes ...
不过,如有需要,也可通过手动方式来构造可重用的类型变量,就像这样:
T = TypeVar('T') # Can be anything S = TypeVar('S', bound=str) # Can be any subtype of str A = TypeVar('A', str, bytes) # Must be exactly str or bytes
类型变量的主要用处是为静态类型检查器提供支持。 它们可作为泛型类型以及泛型函数和类型别名定义的形参。 请参阅
Generic
了解有关泛型类型的更多信息。 泛型函数的作用方式如下:def repeat[T](x: T, n: int) -> Sequence[T]: """Return a list containing n references to x.""" return [x]*n def print_capitalized[S: str](x: S) -> S: """Print x capitalized, and return x.""" print(x.capitalize()) return x def concatenate[A: (str, bytes)](x: A, y: A) -> A: """Add two strings or bytes objects together.""" return x + y
请注意,类型变量可以是 被绑定的 , 被约束的 ,或者两者都不是,但不能既是被绑定的 又是 被约束的。
类型变量的种类是在其通过 类型形参语法 创建时或是在传入
infer_variance=True
时由类型检查器推断得到的。 手动创建的类型变量可通过传入covariant=True
或contravariant=True
被显式地标记为 covariant 或 contravariant。 在默认情况下,手动创建的类型变量为 invariant。 请参阅 PEP 484 和 PEP 695 了解更多细节。绑定类型变量和约束类型变量在几个重要方面具有不同的主义。 使用 绑定 类型变量意味着
TypeVar
将尽可能使用最为专属的类型来解析:x = print_capitalized('a string') reveal_type(x) # revealed type is str class StringSubclass(str): pass y = print_capitalized(StringSubclass('another string')) reveal_type(y) # revealed type is StringSubclass z = print_capitalized(45) # error: int is not a subtype of str
类型变量可以被绑定到具体类型、抽象类型( ABC 或 protocol ),甚至是类型的联合:
# Can be anything with an __abs__ method def print_abs[T: SupportsAbs](arg: T) -> None: print("Absolute value:", abs(arg)) U = TypeVar('U', bound=str|bytes) # Can be any subtype of the union str|bytes V = TypeVar('V', bound=SupportsAbs) # Can be anything with an __abs__ method
但是,如果使用 约束 类型变量,则意味着
TypeVar
只能被解析为恰好是给定的约束之一:a = concatenate('one', 'two') reveal_type(a) # revealed type is str b = concatenate(StringSubclass('one'), StringSubclass('two')) reveal_type(b) # revealed type is str, despite StringSubclass being passed in c = concatenate('one', b'two') # error: type variable 'A' can be either str or bytes in a function call, but not both
在运行时,
isinstance(x, T)
将引发TypeError
。- __name__¶
类型变量的名称。
- __covariant__¶
类型变量是否已被显式地标记为 covariant。
- __contravariant__¶
类型变量是否已被显式地标记为 contravariant。
- __infer_variance__¶
类型变量的种类是否应由类型检查器来推断。
在 3.12 版新加入.
- __bound__¶
类型变量的绑定,如果有的话。
- __constraints__¶
一个包含对类型变量的约束的元组,如果有的话。A tuple containing the constraints of the type variable, if any.
- class typing.TypeVarTuple(name)¶
类型变量元组。 一种启用了 variadic 泛型的专属 类型变量 形式。
类型变量元组可以通过在 类型形参列表 中使用名称前的单个星号 (
*
) 来声明:def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]: return (*tup[1:], tup[0])
或者通过显式地发起调用
TypeVarTuple
构造器:T = TypeVar("T") Ts = TypeVarTuple("Ts") def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]: return (*tup[1:], tup[0])
一个普通类型变量将启用单个类型的形参化。 作为对比,一个类型变量元组通过将 任意 数量的类型变量封包在一个元组中来允许 任意 数量类型的形参化。 例如:
# T is bound to int, Ts is bound to () # Return value is (1,), which has type tuple[int] move_first_element_to_last(tup=(1,)) # T is bound to int, Ts is bound to (str,) # Return value is ('spam', 1), which has type tuple[str, int] move_first_element_to_last(tup=(1, 'spam')) # T is bound to int, Ts is bound to (str, float) # Return value is ('spam', 3.0, 1), which has type tuple[str, float, int] move_first_element_to_last(tup=(1, 'spam', 3.0)) # This fails to type check (and fails at runtime) # because tuple[()] is not compatible with tuple[T, *Ts] # (at least one element is required) move_first_element_to_last(tup=())
请注意解包运算符
*
在tuple[T, *Ts]
中的使用。 在概念上,你可以将Ts
当作一个由类型变量组成的元组(T1, T2, ...)
。 那么tuple[T, *Ts]
就将变为tuple[T, *(T1, T2, ...)]
,这等价于tuple[T, T1, T2, ...]
。 (请注意在旧版本 Python 中,你可能会看到改用Unpack
的写法,如Unpack[Ts]
。)类型变量元组 总是 要被解包。 这有助于区分类型变量元组和普通类型变量:
x: Ts # Not valid x: tuple[Ts] # Not valid x: tuple[*Ts] # The correct way to do it
类型变量元组可被用在与普通类型变量相同的上下文中。 例如,在类定义、参数和返回类型中:
class Array[*Shape]: def __getitem__(self, key: tuple[*Shape]) -> float: ... def __abs__(self) -> "Array[*Shape]": ... def get_shape(self) -> tuple[*Shape]: ...
类型变量元组可以很好地与普通类型变量结合在一起:
class Array[DType, *Shape]: # This is fine pass class Array2[*Shape, DType]: # This would also be fine pass class Height: ... class Width: ... float_array_1d: Array[float, Height] = Array() # Totally fine int_array_2d: Array[int, Height, Width] = Array() # Yup, fine too
但是,请注意在一个类型参数或类型形参列表中最多只能有一个类型变量元组:
x: tuple[*Ts, *Ts] # Not valid class Array[*Shape, *Shape]: # Not valid pass
最后,一个已解包的类型变量元组可以被用作
*args
的类型标注:def call_soon[*Ts]( callback: Callable[[*Ts], None], *args: *Ts ) -> None: ... callback(*args)
相比非解包的
*args
标注 —— 例如*args: int
,它将指明 所有 参数均为int
——*args: *Ts
启用了对*args
中 单个 参数的类型的引用。 在此,这允许我们确保传入call_soon
的*args
的类型与callback
的(位置)参数的类型相匹配。关于类型变量元组的更多细节,请参见 PEP 646。
- __name__¶
类型变量元组的名称。
在 3.11 版新加入.
- class typing.ParamSpec(name, *, bound=None, covariant=False, contravariant=False)¶
形参专属变量。 类型变量 的一个专用版本。
In 类型形参列表,形参规格可以使用两个星号 (
**
) 来声明:type IntFunc[**P] = Callable[P, int]
为了保持与 Python 3.11 及更早版本的兼容性,
ParamSpec
对象也可以这样创建:P = ParamSpec('P')
参数规范变量的存在主要是为了使静态类型检查器受益。 它们被用来将一个可调用对象的参数类型转发给另一个可调用对象的参数类型——这种模式通常出现在高阶函数和装饰器中。 它们只有在
Concatenate
中使用时才有效,或者作为Callable
的第一个参数,或者作为用户定义的泛型的参数。 参见Generic
以了解更多关于泛型的信息。例如,为了给一个函数添加基本的日志记录,我们可以创建一个装饰器
add_logging
来记录函数调用。 参数规范变量告诉类型检查器,传入装饰器的可调用对象和由其返回的新可调用对象有相互依赖的类型参数:from collections.abc import Callable import logging def add_logging[T, **P](f: Callable[P, T]) -> Callable[P, T]: '''A type-safe decorator to add logging to a function.''' def inner(*args: P.args, **kwargs: P.kwargs) -> T: logging.info(f'{f.__name__} was called') return f(*args, **kwargs) return inner @add_logging def add_two(x: float, y: float) -> float: '''Add two numbers together.''' return x + y
如果没有
ParamSpec
,以前注释这个的最简单的方法是使用一个TypeVar
与绑定Callable[..., Any]
。类型检查器不能对
inner
函数进行类型检查,因为*args
和**kwargs
的类型必须是Any
。cast()
在返回inner
函数时,可能需要在add_logging
装饰器的主体中进行,或者必须告诉静态类型检查器忽略return inner
。
- args¶
- kwargs¶
由于
ParamSpec
同时捕获了位置参数和关键字参数,P.args
和P.kwargs
可以用来将ParamSpec
分割成其组成部分。P.args
代表给定调用中的位置参数的元组,只能用于注释*args
。P.kwargs
代表给定调用中的关键字参数到其值的映射,只能用于注释**kwargs
。在运行时,P.args
和P.kwargs
分别是ParamSpecArgs
和ParamSpecKwargs
的实例。
- __name__¶
形参规格的名称。
用
covariant=True
或contravariant=True
创建的参数规范变量可以用来声明协变或逆变泛型类型。 参数bound
也被接受,类似于TypeVar
。 然而这些关键字的实际语义还有待决定。在 3.10 版新加入.
備註
只有在全局范围内定义的参数规范变量可以被 pickle。
也參考
PEP 612 -- 参数规范变量(引入
ParamSpec
和Concatenate
的 PEP)
- typing.ParamSpecArgs¶
- typing.ParamSpecKwargs¶
ParamSpec`的参数和关键字参数属性。``ParamSpec`
的P.args
属性是ParamSpecArgs
的一个实例,P.kwargs
是ParamSpecKwargs
的一个实例。 它们的目的是用于运行时内部检查的,对静态类型检查器没有特殊意义。在这些对象中的任何一个上调用
get_origin()
将返回原始的ParamSpec
:>>> from typing import ParamSpec, get_origin >>> P = ParamSpec("P") >>> get_origin(P.args) is P True >>> get_origin(P.kwargs) is P True
在 3.10 版新加入.
- class typing.TypeAliasType(name, value, *, type_params=())¶
通过
type
语句创建的类型别名的类型。舉例來說:
>>> type Alias = int >>> type(Alias) <class 'typing.TypeAliasType'>
在 3.12 版新加入.
- __name__¶
类型别名的名称:
>>> type Alias = int >>> Alias.__name__ 'Alias'
- __module__¶
类型别名定义所在的模块名称:
>>> type Alias = int >>> Alias.__module__ '__main__'
- __type_params__¶
类型别名的类型形参,或者如果别名不属于泛型则为一个空元组:
>>> type ListOrSet[T] = list[T] | set[T] >>> ListOrSet.__type_params__ (T,) >>> type NotGeneric = int >>> NotGeneric.__type_params__ ()
其他特殊指令¶
这些函数和类不应被直接用作标注。 它们的设计目标是作为创建和声明类型的构件。
- class typing.NamedTuple¶
collections.namedtuple()
的类型版本。用法:
class Employee(NamedTuple): name: str id: int
這等價於:
Employee = collections.namedtuple('Employee', ['name', 'id'])
为字段提供默认值,要在类体内赋值:
class Employee(NamedTuple): name: str id: int = 3 employee = Employee('Guido') assert employee.id == 3
带默认值的字段必须在不带默认值的字段后面。
由此产生的类有一个额外的属性
__annotations__
,给出一个 dict ,将字段名映射到字段类型。(字段名在_fields
属性中,默认值在_field_defaults
属性中,这两者都是namedtuple()
API 的一部分。)NamedTuple
子类也支持文档字符串与方法:class Employee(NamedTuple): """Represents an employee.""" name: str id: int = 3 def __repr__(self) -> str: return f'<Employee {self.name}, id={self.id}>'
NamedTuple
子类也可以为泛型:class Group[T](NamedTuple): key: T group: list[T]
反向兼容用法:
# For creating a generic NamedTuple on Python 3.11 or lower class Group(NamedTuple, Generic[T]): key: T group: list[T] # A functional syntax is also supported Employee = NamedTuple('Employee', [('name', str), ('id', int)])
在 3.6 版的變更: 添加了对 PEP 526 中变量注解句法的支持。
在 3.6.1 版的變更: 添加了对默认值、方法、文档字符串的支持。
在 3.8 版的變更:
_field_types
和__annotations__
属性现已使用常规字典,不再使用OrderedDict
实例。在 3.9 版的變更: 移除了
_field_types
属性, 改用具有相同信息,但更标准的__annotations__
属性。在 3.11 版的變更: 添加对泛型命名元组的支持。
- class typing.NewType(name, tp)¶
用于创建低开销的 独有类型 的辅助类。
NewType
将被类型检查器视为一个独有类型。 但是,在运行时,调用NewType
将原样返回其参数。用法:
UserId = NewType('UserId', int) # Declare the NewType "UserId" first_user = UserId(1) # "UserId" returns the argument unchanged at runtime
- __module__¶
新类型定义所在的模块。
- __name__¶
新类型的名称。
- __supertype__¶
新类型所基于的类型。
在 3.5.2 版新加入.
在 3.10 版的變更:
NewType
现在是一个类而不是函数。
- class typing.Protocol(Generic)¶
协议类的基类。
协议类是这样定义的:
class Proto(Protocol): def meth(self) -> int: ...
这些类主要与静态类型检查器搭配使用,用来识别结构子类型(静态鸭子类型),例如:
class C: def meth(self) -> int: return 0 def func(x: Proto) -> int: return x.meth() func(C()) # Passes static type check
请参阅 PEP 544 了解详情。 使用
runtime_checkable()
装饰的协议类(稍后将介绍)可作为只检查给定属性是否存在,而忽略其类型签名的简单的运行时协议。Protocol 类可以是泛型,例如:
class GenProto[T](Protocol): def meth(self) -> T: ...
在需要兼容 Python 3.11 或更早版本的代码中,可以这样编写泛型协议:
T = TypeVar("T") class GenProto(Protocol[T]): def meth(self) -> T: ...
在 3.8 版新加入.
- @typing.runtime_checkable¶
用于把 Protocol 类标记为运行时协议。
该协议可以与
isinstance()
和issubclass()
一起使用。应用于非协议的类时,会触发TypeError
。该指令支持简易结构检查,与collections.abc
的Iterable
非常类似,只擅长做一件事。 例如:@runtime_checkable class Closable(Protocol): def close(self): ... assert isinstance(open('/some/file'), Closable) @runtime_checkable class Named(Protocol): name: str import threading assert isinstance(threading.Thread(name='Bob'), Named)
備註
runtime_checkable()
将只检查所需方法或属性是否存在,而不检查它们的类型签名或类型。 例如,ssl.SSLObject
是一个类,因此它通过了针对 Callable 的issubclass()
检查。 但是,ssl.SSLObject.__init__
方法的存在只是引发TypeError
并附带更具信息量的消息,因此它无法调用 (实例化)ssl.SSLObject
。備註
针对运行时可检查协议的
isinstance()
检查相比针对非协议类的isinstance()
检查可能会惊人的缓慢。 请考虑在性能敏感的代码中使用替代性写法如hasattr()
调用进行结构检查。在 3.8 版新加入.
在 3.12 版的變更: 现在
isinstance()
的内部实现对于运行时可检查协议的检查会使用inspect.getattr_static()
来查找属性 (在之前版本中,会使用hasattr()
)。 因此,在 Python 3.12+ 上一些以前被认为是运行时可检查协议的实例的对象可能不再被认为是该协议的实例,反之亦反。 大多数用户不太可能受到这一变化的影响。在 3.12 版的變更: 一旦类被创建则运行时可检查协议的成员就会被视为在运行时“已冻结”。 在运行时可检查协议上打上猴子补丁属性仍然有效,但不会影响将对象与协议进行比较的
isinstance()
检查。 请参阅 "Python 3.12 有什么新变化 了解更多细节。
- class typing.TypedDict(dict)¶
把类型提示添加至字典的特殊构造器。在运行时,它是纯
dict
。TypedDict
声明一个字典类型,该类型预期所有实例都具有一组键集,其中,每个键都与对应类型的值关联。运行时不检查此预期,而是由类型检查器强制执行。用法如下:class Point2D(TypedDict): x: int y: int label: str a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
为了在不支持 PEP 526 的旧版 Python 中使用此特性,
TypedDict
支持两种额外的等价语法形式:使用字面量
dict
作为第二个参数:Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
使用关键字参数:
Point2D = TypedDict('Point2D', x=int, y=int, label=str)
自從版本 3.11 後不推薦使用,將會自版本 3.13 中移除。: 使用关键字的语法在 3.11 中被弃用,并且会于 3.13 被移除。同时,该语法可能不被静态类型检查器支持。
当任何一个键不是有效的 标识符 时,例如因为它们是关键字或包含连字符,也应该使用函数式语法。例子:
# raises SyntaxError class Point2D(TypedDict): in: int # 'in' is a keyword x-y: int # name with hyphens # OK, functional syntax Point2D = TypedDict('Point2D', {'in': int, 'x-y': int})
默认情况下,所有的键都必须出现在一个
TypedDict
中。 可以使用NotRequired
将单独的键标记为非必要的:class Point2D(TypedDict): x: int y: int label: NotRequired[str] # Alternative syntax Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': NotRequired[str]})
这意味着一个
Point2D
TypedDict
可以省略label
键。也可以通过全部指定
False
将所有键都标记为默认非必要的:class Point2D(TypedDict, total=False): x: int y: int # Alternative syntax Point2D = TypedDict('Point2D', {'x': int, 'y': int}, total=False)
这意味着一个
Point2D
TypedDict
可以省略任何一个键。 类型检查器只需要支持一个字面的False
或True
作为total
参数的值。True
是默认的,它使类主体中定义的所有项目都是必需的。一个
total=False
TypedDict
中单独的键可以使用Required
标记为必要的:class Point2D(TypedDict, total=False): x: Required[int] y: Required[int] label: str # Alternative syntax Point2D = TypedDict('Point2D', { 'x': Required[int], 'y': Required[int], 'label': str }, total=False)
一个
TypedDict
类型有可能使用基于类的语法从一个或多个其他TypedDict
类型继承。用法:class Point3D(Point2D): z: int
Point3D
有三个项目 :x
,y
和z
。 其等价于定义:class Point3D(TypedDict): x: int y: int z: int
TypedDict
不能从非TypedDict
类继承,除了Generic
。 例如:class X(TypedDict): x: int class Y(TypedDict): y: int class Z(object): pass # A non-TypedDict class class XY(X, Y): pass # OK class XZ(X, Z): pass # raises TypeError
TypedDict
也可以为泛型的:class Group[T](TypedDict): key: T group: list[T]
要创建与 Python 3.11 或更低版本兼容的泛型
TypedDict
,请显式地从Generic
继承:T = TypeVar("T") class Group(TypedDict, Generic[T]): key: T group: list[T]
TypedDict
可以通过注解字典(参见 註釋 (annotation) 最佳實踐 了解更多关于注解的最佳实践)、__total__
、__required_keys__
和__optional_keys__
进行内省。- __total__¶
Point2D.__total__
给出了total
参数的值。 例如:>>> from typing import TypedDict >>> class Point2D(TypedDict): pass >>> Point2D.__total__ True >>> class Point2D(TypedDict, total=False): pass >>> Point2D.__total__ False >>> class Point3D(Point2D): pass >>> Point3D.__total__ True
该属性 只是 反映传给当前
TypedDict
类的``total`` 参数的值,而不反映这个类在语义上是否完整。 例如,一个__total__
被设为 True 的TypedDict
可能有用NotRequired
标记的键,或者它可能继承自另一个设置了total=False
的TypedDict
。 因此,使用__required_keys__
和__optional_keys__
进行内省通常会更好。
- __required_keys__¶
在 3.9 版新加入.
- __optional_keys__¶
Point2D.__required_keys__
和Point2D.__optional_keys__
返回分别包含必要的和非必要的键的frozenset
对象。标记为
Required
的键总是会出现在__required_keys__
中而标记为NotRequired
的键总是会出现在__optional_keys__
中。为了向下兼容 Python 3.10 及更老的版本,还可以使用继承机制在同一个
TypedDict
中同时声明必要和非必要的键。 这是通过声明一个具有total
参数值的TypedDict
然后在另一个TypedDict
中继承它并使用不同的total
值来实现的:>>> class Point2D(TypedDict, total=False): ... x: int ... y: int ... >>> class Point3D(Point2D): ... z: int ... >>> Point3D.__required_keys__ == frozenset({'z'}) True >>> Point3D.__optional_keys__ == frozenset({'x', 'y'}) True
在 3.9 版新加入.
備註
如果使用了
from __future__ import annotations
或者如果以字符串形式给出标注,那么标注不会在定义TypedDict
时被求值。 因此,__required_keys__
和__optional_keys__
所依赖的运行时内省可能无法正常工作,这些属性的值也可能不正确。
更多示例与
TypedDict
的详细规则,详见 PEP 589。在 3.8 版新加入.
在 3.11 版的變更: 增加了对将单独的键标记为
Required
或NotRequired
的支持。 参见 PEP 655。在 3.11 版的變更: 添加对泛型
TypedDict
的支持。
協定¶
下列协议由 typing 模块提供并已全被装饰为 可在运行时检查的
。
- class typing.SupportsAbs¶
一个抽象基类,含一个抽象方法
__abs__
,该方法与其返回类型协变。
- class typing.SupportsBytes¶
一個有抽象方法
__bytes__
的 ABC。
- class typing.SupportsComplex¶
一個有抽象方法
__complex__
的 ABC。
- class typing.SupportsFloat¶
一個有抽象方法
__float__
的 ABC。
- class typing.SupportsIndex¶
一個有抽象方法
__index__
的 ABC。在 3.8 版新加入.
- class typing.SupportsInt¶
一個有抽象方法
__int__
的 ABC。
- class typing.SupportsRound¶
一个抽象基类,含一个抽象方法
__round__
,该方法与其返回类型协变。
与 IO 相关的抽象基类¶
函式與裝飾器¶
- typing.cast(typ, val)¶
把一个值转换为指定的类型。
这会把值原样返回。对类型检查器而言这代表了返回值具有指定的类型,在运行时我们故意没有设计任何检查(我们希望让这尽量快)。
- typing.assert_type(val, typ, /)¶
让静态类型检查器确认 val 具有推断为 typ 的类型。
在运行时这将不做任何事:它会原样返回第一个参数而没有任何检查或附带影响,无论参数的实际类型是什么。
当静态类型检查器遇到对
assert_type()
的调用时,如果该值不是指定的类型则会报错:def greet(name: str) -> None: assert_type(name, str) # OK, inferred type of `name` is `str` assert_type(name, int) # type checker error
此函数适用于确保类型检查器对脚本的理解符合开发者的意图:
def complex_function(arg: object): # Do some complex type-narrowing logic, # after which we hope the inferred type will be `int` ... # Test whether the type checker correctly understands our function assert_type(arg, int)
在 3.11 版新加入.
- typing.assert_never(arg, /)¶
让静态类型检查器确认一行代码是不可达的。
舉例來說:
def int_or_str(arg: int | str) -> None: match arg: case int(): print("It's an int") case str(): print("It's a str") case _ as unreachable: assert_never(unreachable)
在这里,标注允许类型检查器推断最后一种情况永远不会执行,因为
arg
要么是int
要么是str
,而这两种选项都已被之前的情况覆盖了。如果类型检查器发现对
assert_never()
的调用是可达的,它将报告一个错误。 举例来说,如果arg
的类型标注改为int | str | float
,则类型检查器将报告一个错误指出unreachable
为float
类型。 对于通过类型检查的assert_never
调用,参数传入的推断类型必须为兜底类型Never
,而不能为任何其他类型。在运行时,如果调用此函数将抛出一个异常。
也參考
Unreachable Code and Exhaustiveness Checking 有更多关于使用静态类型进行穷尽性检查的信息。
在 3.11 版新加入.
- typing.reveal_type(obj, /)¶
让静态类型检查器显示推测的表达式类型。
当静态类型检查器遇到一个对此函数的调用时,它将发出带有所推测参数类型的诊断信息。 例如:
x: int = 1 reveal_type(x) # Revealed type is "builtins.int"
这在你想要调试你的类型检查器如何处理一段特定代码时很有用处。
在运行时,此函数会将其参数类型打印到
sys.stderr
并不加修改地返回该参数 (以允许该调用在表达式中使用):x = reveal_type(1) # prints "Runtime type is int" print(x) # prints "1"
请注意在运行时类型可能不同于类型静态检查器所推测的类型(明确程度可能更高也可能更低)。
大多数类型检查器都能在任何地方支持
reveal_type()
,即使并未从typing
导入该名称。 不过,从typing
导入该名称将允许你的代码在运行时不会出现运行时错误并能更清晰地传递意图。在 3.11 版新加入.
- @typing.dataclass_transform(*, eq_default=True, order_default=False, kw_only_default=False, frozen_default=False, field_specifiers=(), **kwargs)¶
将一个对象标记为提供类似
dataclass
行为的装饰器。dataclass_transform
可被用于装饰类、元类或本身为装饰器的函数。 使用@dataclass_transform()
将让静态类型检查器知道被装饰的对象会执行以类似@dataclasses.dataclass
的方式来转换类的运行时“魔法”。装饰器函数使用方式的例子:
@dataclass_transform() def create_model[T](cls: type[T]) -> type[T]: ... return cls @create_model class CustomerModel: id: int name: str
在基类上:
@dataclass_transform() class ModelBase: ... class CustomerModel(ModelBase): id: int name: str
在元类上:
@dataclass_transform() class ModelMeta(type): ... class ModelBase(metaclass=ModelMeta): ... class CustomerModel(ModelBase): id: int name: str
上面定义的
CustomerModel
类将被类型检查器视为类似于使用@dataclasses.dataclass
创建的类。 例如,类型检查器将假定这些类具有接受id
和name
的__init__
方法。被装饰的类、元类或函数可以接受以下布尔值参数,类型检查器将假定它们具有与
@dataclasses.dataclass
装饰器相同的效果:init
,eq
,order
,unsafe_hash
,frozen
,match_args
,kw_only
和slots
。 这些参数的值 (True
或False
) 必须可以被静态地求值。传给
dataclass_transform
装饰器的参数可以被用来定制被装饰的类、元类或函数的默认行为:- 參數:
eq_default (bool) -- 指明如果调用方省略了
eq
形参则应将其假定为True
还是False
。 默认为True
。order_default (bool) -- 指明如果调用方省略了
order
形参则应将其假定为True
还是False
。 默认为False
。kw_only_default (bool) -- 指明如果调用方省略了
kw_only
形参则应将其假定为True
还是False
。 默认为False
。frozen_default (bool) -- 指明如果调用方省略了
frozen
形参则应将其假定为True
还是False
。 默认为False
。 .. versionadded:: 3.12field_specifiers (tuple[Callable[..., Any], ...]) -- 指定一个受支持的类或描述字段的函数的静态列表,类似于
dataclasses.field()
。 默认为()
。**kwargs (Any) -- 接受任何其他关键字以便允许可能的未来扩展。
类型检查器能识别下列字段设定器的可选形参:
¶ 形参名称
描述
init
指明字段是否应当被包括在合成的
__init__
方法中。 如果未指明,则init
默认为True
。default
为字段提供默认值。
default_factory
提供一个返回字段默认值的运行时回调。 如果
default
或default_factory
均未指定,则会假定字段没有默认值而在类被实例化时必须提供一个值。factory
字段说明符上
default_factory
形参的别名。kw_only
指明字段是否应被标记为仅限关键字的。 如为
True
,字段将是仅限关键字的。 如为False
,它将不是仅限关键字的。 如未指明,则将使用以dataclass_transform
装饰的对象的kw_only
形参的值,或者如果该值也未指明,则将使用dataclass_transform
上kw_only_default
的值。alias
提供字段的替代名称。 该替代名称会被用于合成的
__init__
方法。在运行时,该装饰器会将其参数记录到被装饰对象的
__dataclass_transform__
属性。 它没有其他的运行时影响。更多細節請見 PEP 681。
在 3.11 版新加入.
- @typing.overload¶
用于创建重载函数和方法的装饰器。
@overload
装饰器允许描述支持多参数类型不同组合的函数和方法。 一系列以@overload
装饰的定义必须带上恰好一个非@overload
装饰的定义(用于同一个函数/方法)。以
@overload
装饰的定义仅对类型检查器有用,因为它们将被非@overload
装饰的定义覆盖。 与此同时,非@overload
装饰的定义将在运行时使用但应被类型检查器忽略。 在运行时,直接调用以@overload
装饰的函数将引发NotImplementedError
。一个提供了比使用联合或类型变量更精确的类型的重载的示例:
@overload def process(response: None) -> None: ... @overload def process(response: int) -> tuple[int, str]: ... @overload def process(response: bytes) -> str: ... def process(response): ... # actual implementation goes here
请参阅 PEP 484 了解更多细节以及与其他类型语义的比较。
在 3.11 版的變更: 现在可以使用
get_overloads()
在运行时内省有重载的函数。
- typing.get_overloads(func)¶
为 func 返回以
@overload
装饰的定义的序列。func 是用于实现过载函数的函数对象。 例如,根据文档中为
@overload
给出的process
定义,get_overloads(process)
将为所定义的三个过载函数返回由三个函数对象组成的序列。 如果在不带过载的函数上调用,get_overloads()
将返回一个空序列。get_overloads()
可被用来在运行时内省一个过载函数。在 3.11 版新加入.
- typing.clear_overloads()¶
清空内部注册表中所有已注册的重载。
这可用于回收注册表所使用的内存。
在 3.11 版新加入.
- @typing.final¶
表示最终化方法和最终化类的装饰器。
以
@final
装饰一个方法将向类型检查器指明该方法不可在子类中被重载。 以@final
装饰一个类表示它不可被子类化。舉例來說:
class Base: @final def done(self) -> None: ... class Sub(Base): def done(self) -> None: # Error reported by type checker ... @final class Leaf: ... class Other(Leaf): # Error reported by type checker ...
这些属性没有运行时检查。详见 PEP 591。
在 3.8 版新加入.
在 3.11 版的變更: 该装饰器现在将尝试在被装饰的对象上设置
__final__
属性为True
。 这样,可以在运行时使用if getattr(obj, "__final__", False)
这样的检查来确定对象obj
是否已被标记为终结。 如果被装饰的对象不支持设置属性,该装饰器将不加修改地返回对象而不会引发异常。
- @typing.no_type_check¶
标明注解不是类型提示的装饰器。
此作用方式类似于类或函数的 decorator。 对于类,它将递归地应用到该类中定义的所有方法和类(但不包括在其超类或子类中定义的方法)。 类型检查器将忽略带有此装饰器的函数或类的所有标注。
@no_type_check
将原地改变被装饰的对象。
- @typing.no_type_check_decorator¶
让其他装饰器具有
no_type_check()
效果的装饰器。本装饰器用
no_type_check()
里的装饰函数打包其他装饰器。
- @typing.override¶
该装饰器指明子类中的某个方法是重载超类中的方法或属性。
如果一个以
@override
装饰的方法实际未重载任何东西则类型检查器应当报告错误。 这有助于防止当基类发生修改而子类未进行相应修改而导致的问题。舉例來說:
class Base: def log_status(self) -> None: ... class Sub(Base): @override def log_status(self) -> None: # Okay: overrides Base.log_status ... @override def done(self) -> None: # Error reported by type checker ...
没有对此特征属性的运行时检查。
该装饰器将尝试在被装饰的对象上设置
__override__
属性为True
。 这样,可以在运行时使用if getattr(obj, "__override__", False)
这样的检查来确定对象obj
是否已被标记为重载。 如果被装饰的对象不支持设置属性,该装饰器将不加修改地返回对象而不会引发异常。更多細節請見 PEP 698。
在 3.12 版新加入.
- @typing.type_check_only¶
将类或函数标记为在运行时不可用的装饰器。
在运行时,该装饰器本身不可用。实现返回的是私有类实例时,它主要是用于标记在类型存根文件中定义的类。
@type_check_only class Response: # private or not available at runtime code: int def get_header(self, name: str) -> str: ... def fetch_response() -> Response: ...
注意,建议不要返回私有类实例,最好将之设为公共类。
内省辅助器¶
- typing.get_type_hints(obj, globalns=None, localns=None, include_extras=False)¶
返回函数、方法、模块、类对象的类型提示的字典。
这往往与
obj.__annotations__
相同。 此外,编码为字符串字面值的前向引用是通过在globals
,locals
和 (如果可用) 类型形参 命名空间中执行求值来处理的。 对于一个类C
,将返回一个由所有__annotations__
与C.__mro__
逆序合并构建而成的字典。本函数会递归地将所有
Annotated[T, ...]
替换为T
,除非include_extras
被设为True
(请参阅Annotated
了解详情)。 例如:class Student(NamedTuple): name: Annotated[str, 'some marker'] assert get_type_hints(Student) == {'name': str} assert get_type_hints(Student, include_extras=False) == {'name': str} assert get_type_hints(Student, include_extras=True) == { 'name': Annotated[str, 'some marker'] }
備註
get_type_hints()
在导入的 类型别名 中不工作,包括前向引用。启用注解的延迟评估( PEP 563 )可能会消除对大多数前向引用的需要。在 3.11 版的變更: 在之前,如果设置了等于
None
的默认值则会为函数和方法标注添加Optional[t]
。 现在标注将被不加修改地返回。
- typing.get_origin(tp)¶
获取一个类型的不带下标的版本:对于
X[Y, Z, ...]
形式的类型对象将返回X
。如果
X
是一个内置类型或collections
类在 typing 模块中的别名,它将被正规化为原始的类。 如果X
是ParamSpecArgs
或ParamSpecKwargs
的实例,则返回下层的ParamSpec
。 对于不受支持的对象将返回None
。舉例:
assert get_origin(str) is None assert get_origin(Dict[str, int]) is dict assert get_origin(Union[int, str]) is Union P = ParamSpec('P') assert get_origin(P.args) is P assert get_origin(P.kwargs) is P
在 3.8 版新加入.
- typing.get_args(tp)¶
获取已执行所有下标的类型参数:对于
X[Y, Z, ...]
形式的类型对象将返回(Y, Z, ...)
。如果
X
是一个并集或是包含在另一个泛型类型中的Literal
,则(Y, Z, ...)
的顺序可能因类型缓存而与原始参数[Y, Z, ...]
存在差异。 对于不受支持的对象将返回()
。舉例:
assert get_args(int) == () assert get_args(Dict[int, str]) == (int, str) assert get_args(Union[int, str]) == (int, str)
在 3.8 版新加入.
常數¶
- typing.TYPE_CHECKING¶
会被第 3 方静态类型检查器假定为
True
的特殊常量。 在运行时将为False
。用法:
if TYPE_CHECKING: import expensive_mod def fun(arg: 'expensive_mod.SomeType') -> None: local_var: expensive_mod.AnotherType = other_fun()
第一个类型注解必须用引号标注,才能把它当作“前向引用”,从而在解释器运行时中隐藏
expensive_mod
引用。局部变量的类型注释不会被评估,因此,第二个注解不需要用引号引起来。備註
若用了
from __future__ import annotations
,函数定义时则不求值注解,直接把注解以字符串形式存在__annotations__
里。这时毋需为注解打引号(见 PEP 563)。在 3.5.2 版新加入.
棄用的別名¶
本模块给标准库中已有的类定义了许多别名,这些别名现已不再建议使用。起初 typing 模块包含这些别名是为了支持用 []
来参数化泛型类。然而,在 Python 3.9 中,对应的已有的类也支持了 []
(参见 PEP 585),因此这些别名了就成了多余的了。
这些多余的类型从 Python 3.9 起被弃用。然而,虽然它们可能会在某一时刻被移除,但目前还没有移除它们的计划。因此,解释器目前不会对这些别名发出弃用警告。
一旦确定了何时这些别名将被移除,解释器将比正式移除之时提前至少两个版本发出弃用警告 (deprecation warning)。但保证至少在 Python 3.14 之前,这些别名仍会留在 typing 模块中,并且不会引发弃用警告。
如果被类型检查器检查的程序旨在运行于 Python 3.9 或更高版本,则鼓励类型检查器标记出这些不建议使用的类型。
內建型別的別名¶
- class typing.Dict(dict, MutableMapping[KT, VT])¶
棄用
dict
的別名。请注意,要注解参数,更推荐使用
Mapping
这样的抽象容器类型,而不是使用dict
或者typing.Dict
。该类型用法如下:
def count_words(text: str) -> Dict[str, int]: ...
在 3.9 版之後被棄用:
builtins.dict
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.List(list, MutableSequence[T])¶
棄用
list
的別名。请注意,要注解参数,更推荐使用
Sequence
或者Iterable
这样的抽象容器类型,而不是使用list
或者typing.List
。该类型用法如下:
def vec2[T: (int, float)](x: T, y: T) -> List[T]: return [x, y] def keep_positives[T: (int, float)](vector: Sequence[T]) -> List[T]: return [item for item in vector if item > 0]
在 3.9 版之後被棄用:
builtins.list
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Set(set, MutableSet[T])¶
棄用
builtins.set
的別名。请注意,要注解参数,更推荐使用
AbstractSet
这样的抽象容器类型,而不是使用set
或者typing.Set
。在 3.9 版之後被棄用:
builtins.set
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.FrozenSet(frozenset, AbstractSet[T_co])¶
棄用
builtins.frozenset
的別名。在 3.9 版之後被棄用:
builtins.frozenset
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- typing.Tuple¶
棄用
tuple
的別名。tuple
和Tuple
是类型系统中的特例;更多详细信息请参见 註釋元組 (tuple)。在 3.9 版之後被棄用:
builtins.tuple
现在支持下标操作([]
)。参见 PEP 585 和 GenericAlias 类型。
- class typing.Type(Generic[CT_co])¶
棄用
type
的別名。有关在类型注解中使用
type
或typing.Type
的详细信息,请参阅 類別物件的型別 。在 3.5.2 版新加入.
在 3.9 版之後被棄用:
builtins.type
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
collections
中型別的別名¶
- class typing.DefaultDict(collections.defaultdict, MutableMapping[KT, VT])¶
棄用
collections.defaultdict
的別名。在 3.5.2 版新加入.
在 3.9 版之後被棄用:
collections.defaultdict
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.OrderedDict(collections.OrderedDict, MutableMapping[KT, VT])¶
棄用
collections.OrderedDict
的別名。在 3.7.2 版新加入.
在 3.9 版之後被棄用:
collections.OrderedDict
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.ChainMap(collections.ChainMap, MutableMapping[KT, VT])¶
棄用
collections.ChainMap
的別名。在 3.6.1 版新加入.
在 3.9 版之後被棄用:
collections.ChainMap
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Counter(collections.Counter, Dict[T, int])¶
棄用
collections.Counter
的別名。在 3.6.1 版新加入.
在 3.9 版之後被棄用:
collections.Counter
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Deque(deque, MutableSequence[T])¶
棄用
collections.deque
的別名。在 3.6.1 版新加入.
在 3.9 版之後被棄用:
collections.deque
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
其他具体类型的别名¶
自從版本 3.8 後不推薦使用,將會自版本 3.13 中移除。:
typing.io
命名空间被弃用并将被删除。 这些类型应该被直接从typing
导入。
- class typing.Pattern¶
- class typing.Match¶
re.compile()
和re.match()
的返回类型的已弃用的别名。这些类型(与对应的函数)是
AnyStr
上的泛型。Pattern
可以被特化为Pattern[str]
或Pattern[bytes]
;Match
可以被特化为Match[str]
或Match[bytes]
。自從版本 3.8 後不推薦使用,將會自版本 3.13 中移除。:
typing.re
命名空间被弃用并将被删除。 这些类型应该被直接从typing
导入。在 3.9 版之後被棄用:
re
模块中的Pattern
与Match
类现已支持[]
。详见 PEP 585 与 GenericAlias 类型。
- class typing.Text¶
棄用
str
的別名。Text
被用来为 Python 2 代码提供向上兼容的路径:在 Python 2 中,Text
是unicode
的别名。使用
Text
时,值中必须包含 unicode 字符串,以兼容 Python 2 和 Python 3:def add_unicode_checkmark(text: Text) -> Text: return text + u' \u2713'
在 3.5.2 版新加入.
在 3.11 版之後被棄用: Python 2 已不再受支持,并且大部分类型检查器也都不再支持 Python 2 代码的类型检查。 目前还没有计划移除该别名,但建议用户使用
str
来代替Text
。
collections.abc
中容器 ABC 的別名¶
- class typing.AbstractSet(Collection[T_co])¶
棄用
collections.abc.Set
的別名。在 3.9 版之後被棄用:
collections.abc.Set
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.ByteString(Sequence[int])¶
该类型代表了
bytes
、bytearray
、memoryview
等字节序列类型。自從版本 3.9 後不推薦使用,將會自版本 3.14 中移除。: 首选
collections.abc.Buffer
,或是bytes | bytearray | memoryview
这样的并集。
- class typing.Collection(Sized, Iterable[T_co], Container[T_co])¶
棄用
collections.abc.Collection
的別名。在 3.6 版新加入.
在 3.9 版之後被棄用:
collections.abc.Collection
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Container(Generic[T_co])¶
棄用
collections.abc.Container
的別名。在 3.9 版之後被棄用:
collections.abc.Container
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.ItemsView(MappingView, AbstractSet[tuple[KT_co, VT_co]])¶
棄用
collections.abc.ItemsView
的別名。在 3.9 版之後被棄用:
collections.abc.ItemsView
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.KeysView(MappingView, AbstractSet[KT_co])¶
棄用
collections.abc.KeysView
的別名。在 3.9 版之後被棄用:
collections.abc.KeysView
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Mapping(Collection[KT], Generic[KT, VT_co])¶
棄用
collections.abc.Mapping
的別名。该类型用法如下:
def get_position_in_index(word_list: Mapping[str, int], word: str) -> int: return word_list[word]
在 3.9 版之後被棄用:
collections.abc.Mapping
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.MappingView(Sized)¶
棄用
collections.abc.MappingView
的別名。在 3.9 版之後被棄用:
collections.abc.MappingView
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.MutableMapping(Mapping[KT, VT])¶
棄用
collections.abc.MutableMapping
的別名。在 3.9 版之後被棄用:
collections.abc.MutableMapping
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.MutableSequence(Sequence[T])¶
棄用
collections.abc.MutableSequence
的別名。在 3.9 版之後被棄用:
collections.abc.MutableSequence
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.MutableSet(AbstractSet[T])¶
棄用
collections.abc.MutableSet
的別名。在 3.9 版之後被棄用:
collections.abc.MutableSet
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Sequence(Reversible[T_co], Collection[T_co])¶
棄用
collections.abc.Sequence
的別名。在 3.9 版之後被棄用:
collections.abc.Sequence
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.ValuesView(MappingView, Collection[_VT_co])¶
棄用
collections.abc.ValuesView
的別名。在 3.9 版之後被棄用:
collections.abc.ValuesView
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
collections.abc
中异步 ABC 的别名¶
- class typing.Coroutine(Awaitable[ReturnType], Generic[YieldType, SendType, ReturnType])¶
棄用
collections.abc.Coroutine
的別名。类型变量的变化形式和顺序与
Generator
的相对应,例如:from collections.abc import Coroutine c: Coroutine[list[str], str, int] # Some coroutine defined elsewhere x = c.send('hi') # Inferred type of 'x' is list[str] async def bar() -> None: y = await c # Inferred type of 'y' is int
在 3.5.3 版新加入.
在 3.9 版之後被棄用:
collections.abc.Coroutine
现在支持下标操作([]
)。参见 PEP 585 和 GenericAlias 类型。
- class typing.AsyncGenerator(AsyncIterator[YieldType], Generic[YieldType, SendType])¶
棄用
collections.abc.AsyncGenerator
的別名。异步生成器可由泛型类型
AsyncGenerator[YieldType, SendType]
注解。例如:async def echo_round() -> AsyncGenerator[int, float]: sent = yield 0 while sent >= 0.0: rounded = await round(sent) sent = yield rounded
与常规生成器不同,异步生成器不能返回值,因此没有
ReturnType
类型参数。 与Generator
类似,SendType
也属于逆变行为。如果生成器只产生值,可将
SendType
设置为None
:async def infinite_stream(start: int) -> AsyncGenerator[int, None]: while True: yield start start = await increment(start)
此外,可用
AsyncIterable[YieldType]
或AsyncIterator[YieldType]
注解生成器的返回类型:async def infinite_stream(start: int) -> AsyncIterator[int]: while True: yield start start = await increment(start)
在 3.6.1 版新加入.
在 3.9 版之後被棄用:
collections.abc.AsyncGenerator
现在支持下标操作([]
)。参见 PEP 585 和 GenericAlias 类型。
- class typing.AsyncIterable(Generic[T_co])¶
棄用
collections.abc.AsyncIterable
的別名。在 3.5.2 版新加入.
在 3.9 版之後被棄用:
collections.abc.AsyncIterable
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.AsyncIterator(AsyncIterable[T_co])¶
棄用
collections.abc.AsyncIterator
的別名。在 3.5.2 版新加入.
在 3.9 版之後被棄用:
collections.abc.AsyncIterator
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Awaitable(Generic[T_co])¶
棄用
collections.abc.Awaitable
的別名。在 3.5.2 版新加入.
在 3.9 版之後被棄用:
collections.abc.Awaitable
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
collections.abc
中其他 ABC 的别名¶
- class typing.Iterable(Generic[T_co])¶
棄用
collections.abc.Iterable
的別名。在 3.9 版之後被棄用:
collections.abc.Iterable
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Iterator(Iterable[T_co])¶
棄用
collections.abc.Iterator
的別名。在 3.9 版之後被棄用:
collections.abc.Iterator
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- typing.Callable¶
棄用
collections.abc.Callable
的別名。有关如何在类型标注中使用
collections.abc.Callable
和typing.Callable
的详细信息请参阅 註釋 callable 物件。在 3.9 版之後被棄用:
collections.abc.Callable
现在支持下标操作([]
)。参见 PEP 585 和 GenericAlias 类型。在 3.10 版的變更:
Callable
現已支援ParamSpec
以及Concatenate
。請參閱 PEP 612 閱讀詳細內容。
- class typing.Generator(Iterator[YieldType], Generic[YieldType, SendType, ReturnType])¶
棄用
collections.abc.Generator
的別名。生成器可以由泛型类型
Generator[YieldType, SendType, ReturnType]
注解。例如:def echo_round() -> Generator[int, float, str]: sent = yield 0 while sent >= 0: sent = yield round(sent) return 'Done'
注意,与 typing 模块里的其他泛型不同,
Generator
的SendType
属于逆变行为,不是协变行为,也是不变行为。如果生成器只产生值,可将
SendType
与ReturnType
设为None
:def infinite_stream(start: int) -> Generator[int, None, None]: while True: yield start start += 1
此外,还可以把生成器的返回类型注解为
Iterable[YieldType]
或Iterator[YieldType]
:def infinite_stream(start: int) -> Iterator[int]: while True: yield start start += 1
在 3.9 版之後被棄用:
collections.abc.Generator
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Hashable¶
棄用
collections.abc.Hashable
的別名。在 3.12 版之後被棄用: 改為直接使用
collections.abc.Hashable
。
- class typing.Reversible(Iterable[T_co])¶
棄用
collections.abc.Reversible
的別名。在 3.9 版之後被棄用:
collections.abc.Reversible
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.Sized¶
棄用
collections.abc.Sized
的別名。在 3.12 版之後被棄用: 改為直接使用
collections.abc.Sized
。
contextlib
ABC 的別名¶
- class typing.ContextManager(Generic[T_co])¶
contextlib.AbstractContextManager
的已弃用的别名。在 3.5.4 版新加入.
在 3.9 版之後被棄用:
contextlib.AbstractContextManager
现在支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
- class typing.AsyncContextManager(Generic[T_co])¶
contextlib.AbstractAsyncContextManager
的已弃用的别名。在 3.6.2 版新加入.
在 3.9 版之後被棄用:
contextlib.AbstractAsyncContextManager
现在 支持下标操作 ([]
)。 参见 PEP 585 和 GenericAlias 类型。
主要特性的弃用时间线¶
typing
的某些特性被弃用,并且可能在将来的 Python 版本中被移除。下表总结了主要的弃用特性。该表可能会被更改,而且并没有列出所有的弃用特性。
特性 |
棄用於 |
计划移除 |
PEP/问题 |
---|---|---|---|
|
3.8 |
3.13 |
|
标准容器的 |
3.9 |
未定(请参阅 棄用的別名 了解详情) |
|
3.9 |
3.14 |
||
3.11 |
未确定 |
||
3.12 |
未确定 |
||
3.12 |
未确定 |