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 提示所指明的。

类型提示可以是简单的类比如 floatstr,它们也可以更为复杂。 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 的實例。在這個範例中,Vectorlist[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.Callabletyping.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) 時,必須使用到兩個值,分別為引述串列以及回傳類別。引數串列必須為一個型別串列:ParamSpecConcatenate 或是一個刪節號 (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__,因為參數規格主要還是用於靜態型別檢查。

在 3.10 版的變更: Generic 現在可以透過參數運算式來進行參數化。詳細內容請見 ParamSpec 以及 PEP 612

一個使用者定義的泛型類別可以將 ABC 作為他們的基底類別,且不會有 metaclass 衝突。泛型的 metaclass 則不支援。參數化泛型的輸出將被存為快取,而在型別模組中多數的型別皆為 hashable 且可以比較相等性。

Any 型別

Any 是一種特別的型別。一個靜態型別檢查器會將每個型別視為可相容於 AnyAny 也可以相容於每個型別。

這意味著如果在一個為 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 靜態型別系統使用標稱子型別。這意味著只有 AB 的子類別時,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) 的型別。

  • 所有型別皆與 Any 相容。

  • Any 相容於所有型別。

在 3.11 版的變更: Any 可以作為一個基礎類別。這對於在任何地方使用鴨子型別或是高度動態的型別,避免型別檢查器的錯誤是非常有用的。

typing.AnyStr

一個不受約束的型別變數

定義:

AnyStr = TypeVar('AnyStr', str, bytes)

AnyStr 是對於函式有用的,他可以接受 strbytes 引數但不可以將此兩種混合。

舉例來說:

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

請注意,儘管他的名稱相近,AnyStrAny 型別無關,更不代表是「任何字串」的意思。尤其,AnyStrstr | 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 可用于与 CallableParamSpec 连用来注解高阶可调用对象,该可象可以添加、移除或转换另一个可调用对象的形参。 使用形式为 Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable]Concatenate 目前仅可用作传给 Callable 的第一个参数。传给 Concatenate 的最后一个形参必须是 ParamSpec 或省略号( ... )。

例如,为了注释一个装饰器 with_lock,它为被装饰的函数提供了 threading.LockConcatenate 可以用来表示 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 版新加入.

也參考

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 版新加入.

在 3.9.1 版的變更: Literal 现在能去除形参的重复。 Literal 对象的相等性比较不再依赖顺序。 现在如果有某个参数不为 hashableLiteral 对象在相等性比较期间将引发 TypeError

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。更多細節請見 TypedDictPEP 655

在 3.11 版新加入.

typing.NotRequired

特殊类型注解构造,用于标记 TypedDict 键为可能不存在的键。

更多細節請見 TypedDictPEP 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]
    

    其中 T1T2 等都是 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 告诉静态类型检查器,某函数:

  1. 返回一个布尔值。

  2. 如果返回值是 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 中的类型映射到 clsself 之后的第二个参数的类型。

简而言之,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]]

实际上,Unpacktyping.TypeVarTuplebuiltins.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=Truecontravariant=True 被显式地标记为 covariant 或 contravariant。 在默认情况下,手动创建的类型变量为 invariant。 请参阅 PEP 484PEP 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__

类型变量的绑定,如果有的话。

在 3.12 版的變更: 对于通过 类型形参语法 创建的类型变量,只有在属性被访问的时候才会对绑定求值,而不是在类型变量被创建的时候 (参见 惰性求值)。

__constraints__

一个包含对类型变量的约束的元组,如果有的话。A tuple containing the constraints of the type variable, if any.

在 3.12 版的變更: 对于通过 类型形参语法 创建的类型变量,只有在属性被访问的时候才会对约束求值,而不是在类型变量被创建的时候 (参见 惰性求值)。

在 3.12 版的變更: 类型变量现在可以通过使用 PEP 695 引入的 类型形参 语法来声明。 增加了 infer_variance 形参。

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 版新加入.

在 3.12 版的變更: 类型变量元组现在可以使用 PEP 695 所引入的 类型形参 语法来声明。

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]

  1. 类型检查器不能对 inner 函数进行类型检查,因为 *args**kwargs 的类型必须是 Any

  2. cast() 在返回 inner 函数时,可能需要在 add_logging 装饰器的主体中进行,或者必须告诉静态类型检查器忽略 return inner

args
kwargs

由于 ParamSpec 同时捕获了位置参数和关键字参数,P.argsP.kwargs 可以用来将 ParamSpec 分割成其组成部分。 P.args 代表给定调用中的位置参数的元组,只能用于注释 *argsP.kwargs 代表给定调用中的关键字参数到其值的映射,只能用于注释 **kwargs。在运行时,P.argsP.kwargs 分别是 ParamSpecArgsParamSpecKwargs 的实例。

__name__

形参规格的名称。

covariant=Truecontravariant=True 创建的参数规范变量可以用来声明协变或逆变泛型类型。 参数 bound 也被接受,类似于 TypeVar。 然而这些关键字的实际语义还有待决定。

在 3.10 版新加入.

在 3.12 版的變更: 形参说明现在可以使用 PEP 695 所引入的 类型形参 语法来声明。

備註

只有在全局范围内定义的参数规范变量可以被 pickle。

也參考

typing.ParamSpecArgs
typing.ParamSpecKwargs

ParamSpec`的参数和关键字参数属性。``ParamSpec`P.args 属性是 ParamSpecArgs 的一个实例,P.kwargsParamSpecKwargs 的一个实例。 它们的目的是用于运行时内部检查的,对静态类型检查器没有特殊意义。

在这些对象中的任何一个上调用 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__
()
__value__

类型别名的值。 它将被 惰性求值,因此别名定义中使用的名称将直到 __value__ 属性被访问时才会被解析:

>>> type Mutually = Recursive
>>> type Recursive = Mutually
>>> Mutually
Mutually
>>> Recursive
Recursive
>>> Mutually.__value__
Recursive
>>> Recursive.__value__
Mutually

其他特殊指令

这些函数和类不应被直接用作标注。 它们的设计目标是作为创建和声明类型的构件。

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.abcIterable 非常类似,只擅长做一件事。 例如:

@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 是一个类,因此它通过了针对 Callableissubclass() 检查。 但是,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 可以省略任何一个键。 类型检查器只需要支持一个字面的 FalseTrue 作为 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 , yz 。 其等价于定义:

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=FalseTypedDict。 因此,使用 __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 版的變更: 增加了对将单独的键标记为 RequiredNotRequired 的支持。 参见 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 相关的抽象基类

class typing.IO
class typing.TextIO
class typing.BinaryIO

泛型 IO[AnyStr] 及其子类 TextIO(IO[str])BinaryIO(IO[bytes]) 表示 I/O 流——例如 open() 返回的对象——的类型。

函式與裝飾器

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,则类型检查器将报告一个错误指出 unreachablefloat 类型。 对于通过类型检查的 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 创建的类。 例如,类型检查器将假定这些类具有接受 idname__init__ 方法。

被装饰的类、元类或函数可以接受以下布尔值参数,类型检查器将假定它们具有与 @dataclasses.dataclass 装饰器相同的效果: init, eq, order, unsafe_hash, frozen, match_args, kw_onlyslots。 这些参数的值 (TrueFalse) 必须可以被静态地求值。

传给 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.12

  • field_specifiers (tuple[Callable[..., Any], ...]) -- 指定一个受支持的类或描述字段的函数的静态列表,类似于 dataclasses.field()。 默认为 ()

  • **kwargs (Any) -- 接受任何其他关键字以便允许可能的未来扩展。

类型检查器能识别下列字段设定器的可选形参:

字段设定器的可识别形参

形参名称

描述

init

指明字段是否应当被包括在合成的 __init__ 方法中。 如果未指明,则 init 默认为 True

default

为字段提供默认值。

default_factory

提供一个返回字段默认值的运行时回调。 如果 defaultdefault_factory 均未指定,则会假定字段没有默认值而在类被实例化时必须提供一个值。

factory

字段说明符上 default_factory 形参的别名。

kw_only

指明字段是否应被标记为仅限关键字的。 如为 True,字段将是仅限关键字的。 如为 False,它将不是仅限关键字的。 如未指明,则将使用以 dataclass_transform 装饰的对象的 kw_only 形参的值,或者如果该值也未指明,则将使用 dataclass_transformkw_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.9 版的變更: 新增 include_extras 參數(如 PEP 593 中所述)。更多資訊請見 Annotated 的文件。

在 3.11 版的變更: 在之前,如果设置了等于 None 的默认值则会为函数和方法标注添加 Optional[t]。 现在标注将被不加修改地返回。

typing.get_origin(tp)

获取一个类型的不带下标的版本:对于 X[Y, Z, ...] 形式的类型对象将返回 X

如果 X 是一个内置类型或 collections 类在 typing 模块中的别名,它将被正规化为原始的类。 如果 XParamSpecArgsParamSpecKwargs 的实例,则返回下层的 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.is_typeddict(tp)

检查一个类型是否为 TypedDict

舉例來說:

class Film(TypedDict):
    title: str
    year: int

assert is_typeddict(Film)
assert not is_typeddict(list | str)

# TypedDict is a factory for creating typed dicts,
# not a typed dict itself
assert not is_typeddict(TypedDict)

在 3.10 版新加入.

class typing.ForwardRef

用于字符串前向引用的内部类型表示的类。

例如,List["SomeClass"] 会被隐式转换为 List[ForwardRef("SomeClass")]ForwardRef 不应由用户来实例化,但可以由内省工具使用。

備註

PEP 585 泛型类型例如 list["SomeClass"] 将不会被隐式地转换为 list[ForwardRef("SomeClass")] 因而将不会自动解析为 list[SomeClass]

在 3.7.4 版新加入.

常數

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 585GenericAlias 类型

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 585GenericAlias 类型

class typing.Set(set, MutableSet[T])

棄用 builtins.set 的別名。

请注意,要注解参数,更推荐使用 AbstractSet 这样的抽象容器类型,而不是使用 set 或者 typing.Set

在 3.9 版之後被棄用: builtins.set 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.FrozenSet(frozenset, AbstractSet[T_co])

棄用 builtins.frozenset 的別名。

在 3.9 版之後被棄用: builtins.frozenset 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

typing.Tuple

棄用 tuple 的別名。

tupleTuple 是类型系统中的特例;更多详细信息请参见 註釋元組 (tuple)

在 3.9 版之後被棄用: builtins.tuple 现在支持下标操作([])。参见 PEP 585GenericAlias 类型

class typing.Type(Generic[CT_co])

棄用 type 的別名。

有关在类型注解中使用 typetyping.Type 的详细信息,请参阅 類別物件的型別

在 3.5.2 版新加入.

在 3.9 版之後被棄用: builtins.type 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

collections 中型別的別名

class typing.DefaultDict(collections.defaultdict, MutableMapping[KT, VT])

棄用 collections.defaultdict 的別名。

在 3.5.2 版新加入.

在 3.9 版之後被棄用: collections.defaultdict 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.OrderedDict(collections.OrderedDict, MutableMapping[KT, VT])

棄用 collections.OrderedDict 的別名。

在 3.7.2 版新加入.

在 3.9 版之後被棄用: collections.OrderedDict 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.ChainMap(collections.ChainMap, MutableMapping[KT, VT])

棄用 collections.ChainMap 的別名。

在 3.6.1 版新加入.

在 3.9 版之後被棄用: collections.ChainMap 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.Counter(collections.Counter, Dict[T, int])

棄用 collections.Counter 的別名。

在 3.6.1 版新加入.

在 3.9 版之後被棄用: collections.Counter 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.Deque(deque, MutableSequence[T])

棄用 collections.deque 的別名。

在 3.6.1 版新加入.

在 3.9 版之後被棄用: collections.deque 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

其他具体类型的别名

自從版本 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 模块中的 PatternMatch 类现已支持 []。详见 PEP 585GenericAlias 类型

class typing.Text

棄用 str 的別名。

Text 被用来为 Python 2 代码提供向上兼容的路径:在 Python 2 中,Textunicode 的别名。

使用 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 585GenericAlias 类型

class typing.ByteString(Sequence[int])

该类型代表了 bytesbytearraymemoryview 等字节序列类型。

自從版本 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 585GenericAlias 类型

class typing.Container(Generic[T_co])

棄用 collections.abc.Container 的別名。

在 3.9 版之後被棄用: collections.abc.Container 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.ItemsView(MappingView, AbstractSet[tuple[KT_co, VT_co]])

棄用 collections.abc.ItemsView 的別名。

在 3.9 版之後被棄用: collections.abc.ItemsView 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.KeysView(MappingView, AbstractSet[KT_co])

棄用 collections.abc.KeysView 的別名。

在 3.9 版之後被棄用: collections.abc.KeysView 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

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 585GenericAlias 类型

class typing.MappingView(Sized)

棄用 collections.abc.MappingView 的別名。

在 3.9 版之後被棄用: collections.abc.MappingView 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.MutableMapping(Mapping[KT, VT])

棄用 collections.abc.MutableMapping 的別名。

在 3.9 版之後被棄用: collections.abc.MutableMapping 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.MutableSequence(Sequence[T])

棄用 collections.abc.MutableSequence 的別名。

在 3.9 版之後被棄用: collections.abc.MutableSequence 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.MutableSet(AbstractSet[T])

棄用 collections.abc.MutableSet 的別名。

在 3.9 版之後被棄用: collections.abc.MutableSet 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.Sequence(Reversible[T_co], Collection[T_co])

棄用 collections.abc.Sequence 的別名。

在 3.9 版之後被棄用: collections.abc.Sequence 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.ValuesView(MappingView, Collection[_VT_co])

棄用 collections.abc.ValuesView 的別名。

在 3.9 版之後被棄用: collections.abc.ValuesView 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

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 585GenericAlias 类型

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 585GenericAlias 类型

class typing.AsyncIterable(Generic[T_co])

棄用 collections.abc.AsyncIterable 的別名。

在 3.5.2 版新加入.

在 3.9 版之後被棄用: collections.abc.AsyncIterable 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.AsyncIterator(AsyncIterable[T_co])

棄用 collections.abc.AsyncIterator 的別名。

在 3.5.2 版新加入.

在 3.9 版之後被棄用: collections.abc.AsyncIterator 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.Awaitable(Generic[T_co])

棄用 collections.abc.Awaitable 的別名。

在 3.5.2 版新加入.

在 3.9 版之後被棄用: collections.abc.Awaitable 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

collections.abc 中其他 ABC 的别名

class typing.Iterable(Generic[T_co])

棄用 collections.abc.Iterable 的別名。

在 3.9 版之後被棄用: collections.abc.Iterable 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.Iterator(Iterable[T_co])

棄用 collections.abc.Iterator 的別名。

在 3.9 版之後被棄用: collections.abc.Iterator 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

typing.Callable

棄用 collections.abc.Callable 的別名。

有关如何在类型标注中使用 collections.abc.Callabletyping.Callable 的详细信息请参阅 註釋 callable 物件

在 3.9 版之後被棄用: collections.abc.Callable 现在支持下标操作([])。参见 PEP 585GenericAlias 类型

在 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 模块里的其他泛型不同, GeneratorSendType 属于逆变行为,不是协变行为,也是不变行为。

如果生成器只产生值,可将 SendTypeReturnType 设为 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 585GenericAlias 类型

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 585GenericAlias 类型

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 585GenericAlias 类型

class typing.AsyncContextManager(Generic[T_co])

contextlib.AbstractAsyncContextManager 的已弃用的别名。

在 3.6.2 版新加入.

在 3.9 版之後被棄用: contextlib.AbstractAsyncContextManager 现在 支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

主要特性的弃用时间线

typing 的某些特性被弃用,并且可能在将来的 Python 版本中被移除。下表总结了主要的弃用特性。该表可能会被更改,而且并没有列出所有的弃用特性。

特性

棄用於

计划移除

PEP/问题

typing.iotyping.re 子模組

3.8

3.13

bpo-38291

标准容器的 typing 版本

3.9

未定(请参阅 棄用的別名 了解详情)

PEP 585

typing.ByteString

3.9

3.14

gh-91896

typing.Text

3.11

未确定

gh-92332

typing.Hashabletyping.Sized

3.12

未确定

gh-94309

typing.TypeAlias

3.12

未确定

PEP 695