typing — 类型提示支持

3.5 版新增。

源代码: Lib/typing.py

注意

Python 运行时不强制执行函数和变量类型标注。它们可以被第三方工具使用,例如 类型检查器、IDE、linter 等。


此模块为类型提示提供运行时支持。

考虑以下函数

def surface_area_of_cube(edge_length: float) -> str:
    return f"The surface area of the cube is {6 * edge_length ** 2}."

函数 surface_area_of_cube 接受一个预期为 float 实例的参数,如 类型提示 edge_length: float 所示。该函数预期返回一个 str 的实例,如 -> str 提示所示。

虽然类型提示可以是简单的类,如 floatstr,但它们也可以更复杂。typing 模块提供了一个更高级类型提示的词汇表。

typing 模块中经常添加新功能。typing_extensions 包为旧版本的 Python 提供了这些新功能的反向移植。

另请参阅

“类型提示速查表”

类型提示的快速概述(托管在 mypy 文档中)

mypy 文档 的“类型系统参考”部分

Python 类型系统是通过 PEP 标准化的,因此本参考应广泛适用于大多数 Python 类型检查器。(某些部分可能仍然是 mypy 特有的。)

“使用 Python 进行静态类型检查”

由社区编写的与类型检查器无关的文档,详细介绍了类型系统功能、有用的类型相关工具和类型最佳实践。

Python 类型系统的规范

Python 类型系统的规范、最新规范可以在 “Python 类型系统规范” 中找到。

类型别名

类型别名是使用 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])

类型别名对于简化复杂的类型签名很有用。例如

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 助手创建不同的类型

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 类型。这允许您在任何预期使用 int 的地方传入 UserId,但会阻止您意外地以无效方式创建 UserId

# 'output' is of type 'int', not 'UserId'
output = UserId(23413) + UserId(54341)

请注意,这些检查仅由静态类型检查器强制执行。在运行时,语句 Derived = NewType('Derived', Base) 将使 Derived 成为一个可调用对象,它立即返回您传递给它的任何参数。这意味着表达式 Derived(some_value) 不会创建新类,也不会引入超出常规函数调用之外的太多开销。

更准确地说,表达式 some_value is Derived(some_value) 在运行时始终为真。

创建 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 类型的值的地方使用。当您想以最小的运行时成本防止逻辑错误时,这很有用。

在 3.5.2 版本中添加。

在 3.10 版本中变更: NewType 现在是一个类而不是一个函数。因此,与常规函数相比,调用 NewType 会有一些额外的运行时成本。

在 3.11 版本中变更: 调用 NewType 的性能已恢复到 Python 3.9 中的水平。

注释可调用对象

函数——或其他可调用 对象——可以使用 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

订阅语法必须始终与两个值一起使用:参数列表和返回类型。参数列表必须是类型列表、ParamSpecConcatenate 或省略号。返回类型必须是单个类型。

如果将文字省略号 ... 作为参数列表给出,则表示具有任意参数列表的可调用对象都是可以接受的

def concat(x: str, y: str) -> str:
    return x + y

x: Callable[..., str]
x = str     # OK
x = concat  # Also OK

Callable 无法表达复杂的签名,例如接受可变数量参数的函数、重载函数 或具有仅限关键字参数的函数。但是,可以通过定义具有 __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

将其他可调用对象作为参数的可调用对象可以使用 ParamSpec 指示其参数类型相互依赖。此外,如果该可调用对象添加或删除了其他可调用对象的参数,则可以使用 Concatenate 运算符。它们分别采用 Callable[ParamSpecVariable, ReturnType]Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType] 的形式。

在 3.10 版本中变更: Callable 现在支持 ParamSpecConcatenate。有关更多详细信息,请参阅 PEP 612

另请参阅

ParamSpecConcatenate 的文档提供了在 Callable 中的用法示例。

泛型

由于无法以泛型方式静态推断容器中保存的对象的类型信息,因此标准库中的许多容器类都支持订阅以表示容器元素的预期类型。

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: ...

泛型函数和类可以通过使用 类型参数语法 进行参数化

from collections.abc import Sequence

def first[T](l: Sequence[T]) -> T:  # Function is generic over the TypeVar "T"
    return l[0]

或者直接使用 TypeVar 工厂

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 中的新增功能。

注释元组

对于 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 赋值上发出错误。类似地,Mapping 只接受两个类型参数:第一个表示键的类型,第二个表示值的类型。

但是,与大多数其他 Python 容器不同,在惯用的 Python 代码中,元组的元素并非都属于同一类型,这很常见。因此,元组在 Python 的类型系统中是特例。 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] 是协变的

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 元类层次结构 的根。

用户定义的泛型类型

用户定义的类可以定义为泛型类。

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 进行参数化的。这也使得 T 在类体内作为类型有效。

泛型类隐式地继承自 Generic。为了与 Python 3.11 及更低版本兼容,也可以显式地继承自 Generic 来表示泛型类。

from typing import TypeVar, Generic

T = TypeVar('T')

class LoggedVar(Generic[T]):
    ...

泛型类具有 __class_getitem__() 方法,这意味着它们可以在运行时进行参数化(例如,下面的 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 不再具有自定义元类。

版本 3.12 中的变化: 对泛型和类型别名的语法支持是 3.12 版本中的新功能。以前,泛型类必须显式地继承自 Generic 或在其基类之一中包含类型变量。

还支持使用 [**P] 形式的参数规范变量为参数表达式定义用户定义的泛型。该行为与上面描述的类型变量一致,因为类型模块将参数规范变量视为一种特殊的类型变量。唯一的例外是可以使用类型列表来替换 ParamSpec

>>> class Z[T, **P]: ...  # T is a TypeVar; P is a ParamSpec
...
>>> Z[int, [dict, float]]
__main__.Z[int, [dict, float]]

也可以使用从 Generic 显式继承来创建基于 ParamSpec 的泛型类。在这种情况下,不使用 **

from typing import ParamSpec, Generic

P = ParamSpec('P')

class Z(Generic[P]):
    ...

TypeVarParamSpec 之间的另一个区别是,只有一个参数规范变量的泛型将接受 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 现在可以基于参数表达式进行参数化。有关更多详细信息,请参阅 ParamSpecPEP 612

用户定义的泛型类可以将 ABC 作为基类,而不会发生元类冲突。不支持泛型元类。泛型参数化的结果会被缓存,并且 typing 模块中的大多数类型都是 可哈希的,并且可以进行相等性比较。

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 的值赋给更精确的类型时,不会执行类型检查。例如,即使 s 被声明为 str 类型并在运行时接收 int 值,静态类型检查器在将 a 赋给 s 时也没有报错!

此外,所有没有返回类型或参数类型的函数都将隐式地默认使用 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 用作“逃生舱”,以便在需要混合动态类型和静态类型代码时使用。

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 以类型安全的方式指示值可以是任何类型。使用 Any 来指示值是动态类型的。

名义子类型与结构子类型

最初,PEP 484 将 Python 静态类型系统定义为使用*名义子类型*。这意味着仅当 AB 的子类时,才允许在需要类 B 的地方使用类 A

此要求以前也适用于抽象基类,例如 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 隐式地视为 SizedIterable[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 模块定义了以下类、函数和装饰器。

特殊类型基元

特殊类型

这些类型可以用作注释中的类型。它们不支持使用 [] 进行订阅。

typing.Any

表示不受约束类型的特殊类型。

  • 每个类型都与 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 非常有用,在这些 API 中,任意的用户生成的字符串可能会产生问题。例如,上面两个生成类型检查器错误的情况可能容易受到 SQL 注入攻击。

有关更多详细信息,请参阅 PEP 675

在版本 3.11 中添加。

typing.Never
typing.NoReturn

NeverNoReturn 表示 底类型,即没有成员的类型。

它们可以用来指示一个函数永远不会返回,例如 sys.exit()

from typing import Never  # or NoReturn

def stop() -> Never:
    raise RuntimeError('no way')

或者定义一个永远不应该被调用的函数,因为它没有有效的参数,例如 assert_never()

from typing import Never  # or NoReturn

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 (or NoReturn)

NeverNoReturn 在类型系统中具有相同的含义,并且静态类型检查器对两者的处理方式相同。

在版本 3.6.2 中添加: 添加了 NoReturn

在版本 3.11 中添加: 添加了 Never

typing.Self

表示当前封闭类的特殊类型。

例如

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 类型。

其他常见用例包括:

  • 用作替代构造函数并返回 cls 参数实例的 classmethod

  • 注释返回自身的 __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 上对于注释使用前向引用的别名特别有用,因为类型检查器很难将这些别名与普通的变量赋值区分开来。

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 的实例,并且本身支持前向引用。请注意,虽然 TypeAliasTypeAliasType 的用途和名称相似,但它们是不同的,后者不是前者的类型。目前没有计划删除 TypeAlias,但鼓励用户迁移到 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 版更改: 现在可以将 Optional 写成 X | None。请参阅 联合类型表达式

typing.Concatenate

用于注释高阶函数的特殊形式。

Concatenate 可以与 CallableParamSpec 结合使用,以注释添加、删除或转换另一个可调用对象的参数的高阶可调用对象。用法形式为 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 版新增。

另请参阅

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 对象的相等性比较不再与顺序相关。如果 Literal 对象的任何参数不可 哈希,则在进行相等性比较时,这些对象现在将引发 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 TypedDicts 有用。有关更多详细信息,请参阅 TypedDictPEP 655

在版本 3.11 中添加。

typing.NotRequired

特殊的类型构造函数,用于将 TypedDict 键标记为可能缺失。

有关更多详细信息,请参阅 TypedDictPEP 655

在版本 3.11 中添加。

typing.Annotated

特殊的类型形式,用于向注释添加上下文特定的元数据。

通过使用注释 Annotated[T, x] 将元数据 x 添加到给定类型 T。使用 Annotated 添加的元数据可以在静态分析工具中或在运行时使用。在运行时,元数据存储在 __metadata__ 属性中。

如果库或工具遇到注释 Annotated[T, x] 并且没有针对元数据的特殊逻辑,则它应该忽略元数据,并将注释简单地视为 T。因此,对于希望将注释用于 Python 静态类型系统范围之外的代码,Annotated 非常有用。

使用 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'>}
    
  • 在运行时,可以通过 __metadata__ 属性检索与 Annotated 类型关联的元数据

    >>> from typing import Annotated
    >>> X = Annotated[int, "very", "important", "metadata"]
    >>> X
    typing.Annotated[int, 'very', 'important', 'metadata']
    >>> X.__metadata__
    ('very', 'important', 'metadata')
    

另请参阅

PEP 593 - 灵活的函数和变量注释

Annotated 引入标准库的 PEP。

3.9 版新增。

typing.TypeGuard

用于标记用户定义的类型保护函数的特殊类型构造函数。

TypeGuard 可用于注释用户定义的类型保护函数的返回类型。 TypeGuard 仅接受一个类型参数。在运行时,以此方式标记的函数应返回一个布尔值。

TypeGuard 旨在有利于*类型缩小*——一种由静态类型检查器使用的技术,用于确定程序代码流中表达式的更精确类型。通常,类型缩小是通过分析条件代码流并将缩小应用于代码块来完成的。这里的条件表达式有时被称为“类型保护”

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``.
        ...

有时,使用用户定义的布尔函数作为类型守卫会很方便。此类函数应使用 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,则 argTypeA 缩小为 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]]

实际上,在 typing.TypeVarTuplebuiltins.tuple 类型的上下文中,Unpack 可以与 * 互换使用。您可能会看到在旧版本的 Python 中显式使用了 Unpack,因为在某些地方不能使用 *

# 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]): ...

有关使用 Unpack 进行 **kwargs 键入的更多详细信息,请参阅 PEP 692

在版本 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 来显式标记手动创建的类型变量是协变的还是逆变的。默认情况下,手动创建的类型变量是不变的。有关更多详细信息,请参阅 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 或协议),甚至是类型的联合

# 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__

类型变量是否已显式标记为协变。

__contravariant__

类型变量是否已显式标记为逆变。

__infer_variance__

类型变量的方差是否应由类型检查器推断。

在 3.12 版本中添加。

__bound__

类型变量的边界(如果有)。

在 3.12 版本中更改: 对于通过 类型参数语法 创建的类型变量,仅在访问属性时才评估边界,而不是在创建类型变量时评估(请参阅 惰性求值)。

__constraints__

一个元组,包含类型变量的约束(如果有)。

在 3.12 版本中更改: 对于通过 类型参数语法 创建的类型变量,仅在访问属性时才评估约束,而不是在创建类型变量时评估(请参阅 惰性求值)。

在 3.12 版本中更改: 现在可以使用 PEP 695 引入的 类型参数 语法来声明类型变量。添加了 infer_variance 参数。

class typing.TypeVarTuple(name)

类型变量元组。一种特殊形式的 类型变量,它支持可变泛型。

可以在 类型参数列表 中使用名称前的单个星号 (*) 声明类型变量元组

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)

参数规范变量。 类型变量 的一种特殊版本。

类型参数列表 中,可以使用两个星号 (**) 声明参数规范

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,以前注释此代码的最简单方法是使用绑定了 Callable[..., Any]TypeVar。但是,这会导致两个问题

  1. 类型检查器无法对 inner 函数进行类型检查,因为 *args**kwargs 必须键入 Any

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

args
kwargs

由于 ParamSpec 捕获了位置参数和关键字参数,因此可以使用 P.argsP.kwargsParamSpec 拆分为其组成部分。 P.args 表示给定调用中位置参数的元组,并且只能用于注释 *argsP.kwargs 表示给定调用中关键字参数与其值的映射,并且只能用于注释 **kwargs。 这两个属性都要求注释的参数在作用域内。 在运行时,P.argsP.kwargs 分别是 ParamSpecArgsParamSpecKwargs 的实例。

__name__

参数规范的名称。

使用 covariant=Truecontravariant=True 创建的参数规范变量可用于声明协变或逆变泛型类型。 与 TypeVar 类似,也接受 bound 参数。 但是,这些关键字的实际语义尚未确定。

3.10 版新增。

在版本 3.12 中更改: 现在可以使用 PEP 695 引入的 类型参数 语法声明参数规范。

注意

只有在全局作用域中定义的参数规范变量才能被序列化。

另请参阅

typing.ParamSpecArgs
typing.ParamSpecKwargs

ParamSpec 的参数和关键字参数属性。 ParamSpecP.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__,它提供了一个字典,将字段名称映射到字段类型。 (字段名称位于 _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()(稍后描述)装饰的协议类充当简单的运行时协议,仅检查给定属性的存在,而忽略其类型签名。

协议类可以是泛型的,例如

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

将协议类标记为运行时协议。

此类协议可以与 isinstance()issubclass() 一起使用。当应用于非协议类时,这会引发 TypeError。这允许进行简单的结构检查,非常类似于 collections.abc 中的“一招鲜” pony,例如 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 是一个类,因此它通过了针对 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 是默认值,它要求类体中定义的所有项都必须存在。

可以使用 Requiredtotal=FalseTypedDict 的各个键标记为必需。

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 有三个项:xyz。它等效于以下定义

class Point3D(TypedDict):
    x: int
    y: int
    z: int

除了 Generic 之外,TypedDict 不能从非 TypedDict 类继承。例如

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]

可以通过 annotations 字典(有关 annotations 最佳实践的更多信息,请参阅 Annotations 最佳实践)、__total____required_keys____optional_keys__TypedDict 进行自省。

__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__ 设置为 TrueTypedDict 可能具有标记为 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 中声明必需键和非必需键。这是通过声明一个 TypedDict,其中 total 参数有一个值,然后在另一个 total 参数具有不同值的 TypedDict 中继承它来完成的

>>> 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 或者 annotations 以字符串形式给出,则在定义 TypedDict 时不会评估 annotations。因此,__required_keys____optional_keys__ 所依赖的运行时自省可能无法正常工作,并且属性的值可能不正确。

有关使用 TypedDict 的更多示例和详细规则,请参阅 PEP 589

3.8 版新增。

版本 3.11 中的变化: 添加了对将单个键标记为 RequiredNotRequired 的支持。请参阅 PEP 655

版本 3.11 中的变化: 添加了对泛型 TypedDict 的支持。

协议

以下协议由 typing 模块提供。所有协议都使用 @runtime_checkable 进行装饰。

class typing.SupportsAbs

一个 ABC,带有一个抽象方法 __abs__,该方法在其返回类型中是协变的。

class typing.SupportsBytes

一个 ABC,带有一个抽象方法 __bytes__

class typing.SupportsComplex

一个 ABC,带有一个抽象方法 __complex__

class typing.SupportsFloat

一个 ABC,带有一个抽象方法 __float__

class typing.SupportsIndex

一个 ABC,带有一个抽象方法 __index__

3.8 版新增。

class typing.SupportsInt

一个 ABC,带有一个抽象方法 __int__

class typing.SupportsRound

一个 ABC,带有一个抽象方法 __round__,该方法在其返回类型中是协变的。

用于处理 IO 的 ABC

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)

这里,注释允许类型检查器推断最后一种情况永远不会执行,因为 argintstr,并且这两种选择都被前面的情况覆盖了。

如果类型检查器发现对 assert_never() 的调用是可达的,它将发出错误。例如,如果 arg 的类型注释是 int | str | float,则类型检查器将发出错误,指出 unreachable 的类型为 float。为了使对 assert_never 的调用通过类型检查,传入参数的推断类型必须是底部类型 Never,而不是其他任何类型。

在运行时,这会在调用时抛出异常。

另请参阅

不可达代码和穷举检查 包含有关使用静态类型进行穷举检查的更多信息。

在版本 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 装饰器上的效果相同:initeqorderunsafe_hashfrozenmatch_argskw_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

    在 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

用于指示注释不是类型提示的装饰器。

这可以用作类或函数 装饰器。对于类,它递归地应用于该类中定义的所有方法和类(但不适用于在其超类或子类中定义的方法)。类型检查器将忽略带有此装饰器的函数或类中的所有注释。

@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__ 相同,但此函数会对注释字典进行以下更改:

  • 通过在 globalnslocalns 和(如果适用)obj类型参数 命名空间中评估前向引用(编码为字符串字面量或 ForwardRef 对象)来处理它们。如果未提供 globalnslocalns,则会从 obj 推断出适当的命名空间字典。

  • None 将替换为 types.NoneType

  • 如果已将 @no_type_check 应用于 obj,则返回空字典。

  • 如果 obj 是类 C,则该函数返回一个字典,该字典将 C 的基类的注释与 C 上的注释直接合并。这是通过遍历 C.__mro__ 并迭代组合 __annotations__ 字典来完成的。出现在 方法解析顺序 中较早的类上的注释始终优先于出现在方法解析顺序中较晚的类上的注释。

  • 该函数递归地将所有出现的 Annotated[T, ...] 替换为 T,除非 include_extras 设置为 True(有关更多信息,请参阅 Annotated)。

另请参阅 inspect.get_annotations(),这是一个较低级别的函数,它更直接地返回注释。

注意

如果 obj 的注释中的任何前向引用无法解析或不是有效的 Python 代码,则此函数将引发异常,例如 NameError。例如,这可能发生在包含前向引用的导入 类型别名 中,或者在 if TYPE_CHECKING 下导入的名称中。

3.9 版更改: 作为 PEP 593 的一部分,添加了 include_extras 参数。有关更多信息,请参阅 Annotated 上的文档。

3.11 版更改: 以前,如果设置的默认值等于 None,则会为函数和方法注释添加 Optional[t]。现在,注释将按原样返回。

typing.get_origin(tp)

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

如果 X 是内置类或 collections 类的类型模块别名,它将被标准化为原始类。如果 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

一个特殊的常量,第三方静态类型检查器假定它为 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 开始,这些冗余类型已被弃用。但是,虽然这些别名将来可能会被删除,但目前还没有计划删除它们。因此,解释器目前不会针对这些别名发出弃用警告。

如果将来决定删除这些已弃用的别名,则解释器将在删除之前至少发布两个版本的弃用警告。保证这些别名至少在 Python 3.14 之前会保留在 typing 模块中,并且不会发出弃用警告。

如果类型检查器正在检查的程序的目标 Python 版本为 3.9 或更高版本,则鼓励它们标记出已弃用类型的使用。

内置类型的别名

class typing.Dict(dict, MutableMapping[KT, VT])

dict 的已弃用别名。

请注意,要注释参数,最好使用抽象集合类型(例如 Mapping),而不是使用 dicttyping.Dict

此类型可以按如下方式使用

def count_words(text: str) -> Dict[str, int]:
    ...

3.9 版后已弃用: builtins.dict 现在支持下标([])。请参阅 PEP 585泛型别名类型

class typing.List(list, MutableSequence[T])

list 的已弃用别名。

请注意,要注释参数,最好使用抽象集合类型(例如 SequenceIterable),而不是使用 listtyping.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泛型别名类型

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

builtins.set 的已弃用别名。

请注意,要注释参数,最好使用抽象集合类型(例如 AbstractSet),而不是使用 settyping.Set

3.9 版后已弃用: builtins.set 现在支持下标([])。请参阅 PEP 585泛型别名类型

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

builtins.frozenset 的已弃用别名。

3.9 版后已弃用: builtins.frozenset 现在支持下标([])。请参阅 PEP 585泛型别名类型

typing.Tuple

tuple 的已弃用别名。

tupleTuple 在类型系统中是特例;有关详细信息,请参阅对元组进行标注

3.9 版后已弃用: builtins.tuple 现在支持下标 ([])。请参阅 PEP 585泛型别名类型

class typing.Type(Generic[CT_co])

type 的已弃用别名。

有关在类型标注中使用 typetyping.Type 的详细信息,请参阅类对象的类型

在 3.5.2 版本中添加。

3.9 版后已弃用: builtins.type 现在支持下标 ([])。请参阅 PEP 585泛型别名类型

collections 中类型的别名

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

collections.defaultdict 的已弃用别名。

在 3.5.2 版本中添加。

3.9 版后已弃用: collections.defaultdict 现在支持下标 ([])。请参阅 PEP 585泛型别名类型

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

collections.OrderedDict 的已弃用别名。

3.7.2 版新增。

3.9 版后已弃用: collections.OrderedDict 现在支持下标 ([])。请参阅 PEP 585泛型别名类型

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

collections.ChainMap 的已弃用别名。

3.6.1 版新增。

3.9 版后已弃用: collections.ChainMap 现在支持下标 ([])。请参阅 PEP 585泛型别名类型

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

collections.Counter 的已弃用别名。

3.6.1 版新增。

3.9 版后已弃用: collections.Counter 现在支持下标 ([])。请参阅 PEP 585泛型别名类型

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

collections.deque 的已弃用别名。

3.6.1 版新增。

3.9 版后已弃用: collections.deque 现在支持下标 ([])。请参阅 PEP 585泛型别名类型

其他具体类型的别名

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 585泛型别名类型

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 585泛型别名类型

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 585泛型别名类型

class typing.Container(Generic[T_co])

已弃用的 collections.abc.Container 的别名。

3.9 版后已弃用: collections.abc.Container 现在支持下标 ([])。参见 PEP 585泛型别名类型

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

已弃用的 collections.abc.ItemsView 的别名。

3.9 版后已弃用: collections.abc.ItemsView 现在支持下标 ([])。参见 PEP 585泛型别名类型

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

已弃用的 collections.abc.KeysView 的别名。

3.9 版后已弃用: collections.abc.KeysView 现在支持下标 ([])。参见 PEP 585泛型别名类型

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泛型别名类型

class typing.MappingView(Sized)

已弃用的 collections.abc.MappingView 的别名。

3.9 版后已弃用: collections.abc.MappingView 现在支持下标 ([])。 参见 PEP 585泛型别名类型

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

已弃用的 collections.abc.MutableMapping 的别名。

3.9 版后已弃用: collections.abc.MutableMapping 现在支持下标 ([])。 参见 PEP 585泛型别名类型

class typing.MutableSequence(Sequence[T])

已弃用的 collections.abc.MutableSequence 的别名。

3.9 版后已弃用: collections.abc.MutableSequence 现在支持下标 ([])。 参见 PEP 585泛型别名类型

class typing.MutableSet(AbstractSet[T])

已弃用的 collections.abc.MutableSet 的别名。

3.9 版后已弃用: collections.abc.MutableSet 现在支持下标 ([])。 参见 PEP 585泛型别名类型

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

已弃用的 collections.abc.Sequence 的别名。

3.9 版后已弃用: collections.abc.Sequence 现在支持下标 ([])。 参见 PEP 585泛型别名类型

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

已弃用的 collections.abc.ValuesView 的别名。

3.9 版后已弃用: collections.abc.ValuesView 现在支持下标 ([])。 参见 PEP 585泛型别名类型

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泛型别名类型

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泛型别名类型

class typing.AsyncIterable(Generic[T_co])

已弃用的别名,指向 collections.abc.AsyncIterable

在 3.5.2 版本中添加。

3.9 版后已弃用: collections.abc.AsyncIterable 现在支持下标 ([])。 参见 PEP 585泛型别名类型

class typing.AsyncIterator(AsyncIterable[T_co])

已弃用的别名,指向 collections.abc.AsyncIterator

在 3.5.2 版本中添加。

3.9 版后已弃用: collections.abc.AsyncIterator 现在支持下标 ([])。 参见 PEP 585泛型别名类型

class typing.Awaitable(Generic[T_co])

已弃用的别名,指向 collections.abc.Awaitable

在 3.5.2 版本中添加。

3.9 版后已弃用: collections.abc.Awaitable 现在支持下标 ([])。 参见 PEP 585泛型别名类型

指向 collections.abc 中其他 ABC 的别名

class typing.Iterable(Generic[T_co])

已弃用的别名,指向 collections.abc.Iterable

3.9 版后已弃用: collections.abc.Iterable 现在支持下标 ([])。 参见 PEP 585泛型别名类型

class typing.Iterator(Iterable[T_co])

已弃用的别名,指向 collections.abc.Iterator

3.9 版后已弃用: collections.abc.Iterator 现在支持下标 ([])。 参见 PEP 585泛型别名类型

typing.Callable

已弃用的别名,指向 collections.abc.Callable

有关如何在类型标注中使用 collections.abc.Callabletyping.Callable 的详细信息,请参阅 标注可调用对象

3.9 版后已弃用: collections.abc.Callable 现在支持下标 ([])。 参见 PEP 585泛型别名类型

在 3.10 版更改: Callable 现在支持 ParamSpecConcatenate。 更多详细信息,请参阅 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 585泛型别名类型

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泛型别名类型

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泛型别名类型

class typing.AsyncContextManager(Generic[T_co])

已弃用的 contextlib.AbstractAsyncContextManager 的别名。

3.6.2 版中添加。

自 3.9 版起弃用: contextlib.AbstractAsyncContextManager 现在支持下标 ([])。请参阅 PEP 585泛型别名类型

主要功能的弃用时间表

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