typing —— 对类型提示的支持

3.5 新版功能.

源代码: Lib/typing.py

注解

Python 运行时不强制要求函数与变量类型注解。它们可被类型检查器、IDE、错误检查器等第三方工具使用。


这个模块提供对类型提示的运行时支持。最基本的支持包括 Any , Union , Callable , TypeVar , 和 Generic 。关于完整的规范,请参考 PEP 484 。关于类型提示的简化介绍,请参考 PEP 483

下面的函数接收与返回的都是字符串,注解方式如下:

def greeting(name: str) -> str:
    return 'Hello ' + name

greeting 函数中,参数 name 的类型应是 str,返回类型是 str。子类型也可以作为参数。

新的功能频繁地被添加到 typing 模块中。typing_extensions 包提供了这些新功能对旧版本 Python 的向后移植。

参见

For a quick overview of type hints, refer to this cheat sheet.

The "Type System Reference" section of https://mypy.readthedocs.io/ -- since the Python typing system is standardised via PEPs, this reference should broadly apply to most Python type checkers, although some parts may still be specific to mypy.

位于 https://typing.readthedocs.io/ 的文档提供有用的类型系统功能参考, 类型关系工具以及练习

相关的 PEP

自从在 PEP 484PEP 483 中首次引入类型提示以来,一些 PEP 已经修改和增强了 Python 的类型注释框架。包括:

类型别名

类型别名是通过为类型赋值为指定的别名来定义的。 在本例中,Vectorlist[float] 将被视为可互换的同义词:

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

ConnectionOptions = dict[str, str]
Address = tuple[str, int]
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:
    ...

注意,None 能作为类型提示是一个特例,它会被自动替换为 type(None)

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) 表达式总为 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

注解

请记住使用类型别名将声明两个类型是相互 等价 的。 使用 Alias = Original 将使静态类型检查器在任何情况下都把 Alias 视为与 Original 完全等价。 这在你想要简化复杂的类型签名时会很有用处。

反之,NewType 声明把一种类型当作另一种类型的 子类型Derived = NewType('Derived', Original) 时,静态类型检查器把 Derived 当作 Original子类 ,即,Original 类型的值不能用在预期 Derived 类型的位置。这种方式适用于以最小运行时成本防止逻辑错误。

3.5.2 新版功能.

在 3.10 版更改: NewType 现在是一个类而不是一个函数。 在调用 NewType 而不是普通的函数时,会有一些额外的运行时间成本。 然而,这种开销将在 3.11.0 中减少。

可调对象(Callable)

预期特定签名回调函数的框架可以用 Callable[[Arg1Type, Arg2Type], ReturnType] 实现类型提示。

例如:

from collections.abc import Callable

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

无需指定调用签名,用省略号字面量替换类型提示里的参数列表: Callable[..., ReturnType],就可以声明可调对象的返回类型。

以其他可调用对象为参数的可调用对象可以使用 ParamSpec 来表明其参数类型是相互依赖的。 此外,如果该可调用对象增加或删除了其他可调用对象的参数,可以使用 Concatenate 操作符。 它们分别采取 Callable[ParamSpecVariable, ReturnType]Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType] 的形式。

在 3.10 版更改: Callable 现在支持 ParamSpecConcatenate。 详情见 PEP 612

参见

ParamSpecConcatenate 的文档提供了在 Callable 中使用的例子。

泛型(Generic)

容器中,对象的类型信息不能以泛型方式静态推断,因此,抽象基类扩展支持下标,用于表示容器元素的预期类型。

from collections.abc import Mapping, Sequence

def notify_by_email(employees: Sequence[Employee],
                    overrides: Mapping[str, str]) -> None: ...

要参数化泛型,可使用 typing 中的 TypeVar 工厂。

from collections.abc import Sequence
from typing import TypeVar

T = TypeVar('T')      # Declare type variable

def first(l: Sequence[T]) -> T:   # Generic function
    return l[0]

用户定义的泛型类型

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

from typing import TypeVar, Generic
from logging import Logger

T = TypeVar('T')

class LoggedVar(Generic[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)

Generic[T] 是定义类 LoggedVar 的基类,该类使用单类型参数 T。在该类体内,T 是有效的类型。

The Generic base class defines __class_getitem__() so that LoggedVar[T] is valid as a type:

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

T = TypeVar('T', contravariant=True)
B = TypeVar('B', bound=Sequence[bytes], covariant=True)
S = TypeVar('S', int, str)

class WeirdTrio(Generic[T, B, S]):
    ...

Generic 类型变量的参数应各不相同。下列代码就是无效的:

from typing import TypeVar, Generic
...

T = TypeVar('T')

class Pair(Generic[T, T]):   # INVALID
    ...

您可以通过 Generic 来使用多重继承:

from collections.abc import Sized
from typing import TypeVar, Generic

T = TypeVar('T')

class LinkedList(Sized, Generic[T]):
    ...

继承自泛型类时,可以修正某些类型变量:

from collections.abc import Mapping
from typing import TypeVar

T = TypeVar('T')

class MyDict(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
from typing import TypeVar
S = TypeVar('S')
Response = Iterable[S] | int

# Return type here is same as Iterable[str] | int
def response(query: str) -> Response[str]:
    ...

T = TypeVar('T', int, float, complex)
Vec = Iterable[tuple[T, T]]

def inproduct(v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]]
    return sum(x*y for x, y in v)

在 3.7 版更改: Generic 不再支持自定义元类。

用户定义的参数表达式的泛型也通过 Generic[P] 形式的参数规范变量来支持。该行为与上面描述的类型变量一致,因为参数规范变量被类型化模块视为一个专门的类型变量。 这方面的一个例外是,一个类型列表可以用来替代 ParamSpec:

>>> from typing import Generic, ParamSpec, TypeVar

>>> T = TypeVar('T')
>>> P = ParamSpec('P')

>>> class Z(Generic[T, P]): ...
...
>>> Z[int, [dict, float]]
__main__.Z[int, (<class 'dict'>, <class 'float'>)]

此外,一个只有一个参数规范变量的泛型将接受表格 X[[Type1, Type2, ...]] 中的参数列表,出于美观的考虑也包括 X[Type1, Type2, ...] 。 在内部,后者被转换为前者,所以下面的内容是等价的:

>>> class X(Generic[P]): ...
...
>>> X[int, str]
__main__.X[(<class 'int'>, <class 'str'>)]
>>> X[[int, str]]
__main__.X[(<class 'int'>, <class 'str'>)]

请注意,带有 ParamSpec 的泛型在某些情况下可能不会有正确的``__parameters__``,因为它们主要用于静态类型检查。

在 3.10 版更改: Generic 现在可以通过参数表达式进行参数化。参见 ParamSpecPEP 612 以了解更多细节。

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

Any 类型

Any 是一种特殊的类型。静态类型检查器认为所有类型均与 Any 兼容,同样,Any 也与所有类型兼容。

也就是说,可对 Any 类型的值执行任何操作或方法调用,并赋值给任意变量:

from typing import Any

a: Any = None
a = []          # OK
a = 2           # OK

s: str = ''
s = a           # OK

def foo(item: Any) -> int:
    # Passes type checking; 'item' could be any type,
    # and that type might have a 'bar' method
    item.bar()
    ...

注意,Any 类型的值赋给更精确的类型时,不执行类型检查。例如,把 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 当作 应急出口

Anyobject 的区别。与 Any 相似,所有类型都是 object 的子类型。然而,与 Any 不同,object 不可逆: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,说明值是动态类型。

名义子类型 vs 结构子类型

最初 PEP 484 将 Python 静态类型系统定义为使用 名义子类型。这意味着当且仅当类 AB 的子类时,才满足有类 B 预期时使用类 A

此项要求以前也适用于抽象基类,例如,Iterable 。这种方式的问题在于,定义类时必须显式说明,既不 Pythonic,也不是动态类型式 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 ,用户可以定义新的自定义协议(见下文中的例子)。

模块内容

本模块定义了下列类、函数和修饰器。

注解

本模块定义了一些类型,作为标准库中已有的类的子类,从而可以让 Generic 支持 [] 中的类型变量。Python 3.9 中,这些标准库的类已支持 [] ,因此,这些类型就变得冗余了。

Python 3.9 弃用了这些冗余类型,但解释器并未提供相应的弃用警告。标记弃用类型的工作留待支持 Python 3.9 及以上版本的类型检查器实现。

Python 3.9.0 发布五年后的首个 Python 发行版将从 typing 模块中移除这些弃用类型。详见 PEP 585标准集合的类型提示泛型》。

特殊类型原语

特殊类型

这些类型可用于类型注解,但不支持 []

typing.Any

特殊类型,表示没有约束的类型。

  • 所有类型都与 Any 兼容。

  • Any 与所有类型都兼容。

typing.NoReturn

标记没有返回值的函数的特殊类型。例如:

from typing import NoReturn

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

3.5.4 新版功能.

3.6.2 新版功能.

typing.TypeAlias

用于显式声明 类型别名 的特殊标注。 例如:

from typing import TypeAlias

Factors: TypeAlias = list[int]

关于显式类型别名的更多细节,请参见 PEP 613

3.10 新版功能.

特殊形式

可用于类型注解,且支持 [] ,每种形式都有其独特的句法。

typing.Tuple

元组类型; Tuple[X, Y] 是二项元组类型,第一个元素的类型是 X,第二个元素的类型是 Y。空元组的类型可写为 Tuple[()]

例:Tuple[T1, T2] 是二项元组,类型变量分别为 T1 和 T2。Tuple[int, float, str] 是由整数、浮点数、字符串组成的三项元组。

可用省略号字面量指定同质变长元组,例如,Tuple[int, ...]TupleTuple[Any, ...] 等价,也与 tuple 等价。

3.9 版后已移除: builtins.tuple 现在支持下标操作([])。参见 PEP 585GenericAlias 类型

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

可调类型; Callable[[int], str] 是把(int)转为 str 的函数。

下标句法必须与参数列表和返回类型这两个值一起使用。参数列表只能是类型列表或省略号;返回类型只能是单一类型。

没有说明可选参数或关键字参数的句法;这类函数类型很少用作回调类型。Callable[..., ReturnType] (省略号字面量)可用于为接受任意数量参数,并返回 ReturnType 的可调对象提供类型提示。纯 Callable 等价于 Callable[..., Any],进而等价于 collections.abc.Callable

以其他可调用对象为参数的可调用对象可以使用 ParamSpec 来表明其参数类型是相互依赖的。 此外,如果该可调用对象增加或删除了其他可调用对象的参数,可以使用 Concatenate 操作符。 它们分别采取 Callable[ParamSpecVariable, ReturnType]Callable[Concatenate[Arg1Type, Arg2Type, ..., ParamSpecVariable], ReturnType] 的形式。

3.9 版后已移除: collections.abc.Callable 现在支持下标操作([])。参见 PEP 585GenericAlias 类型

在 3.10 版更改: Callable 现在支持 ParamSpecConcatenate。 详情见 PEP 612

参见

ParamSpecConcatenate 的文档提供了使用 Callable 的例子。

typing.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, ParamSpec, TypeVar

P = ParamSpec('P')
R = TypeVar('R')

# Use this lock to ensure that only one thread is executing a function
# at any time.
my_lock = Lock()

def with_lock(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 新版功能.

参见

class typing.Type(Generic[CT_co])

C 注解的变量可以接受类型 C 的值。反之,用 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 BasicUser(User): ...
class ProUser(User): ...
class TeamUser(User): ...

# Accepts User, BasicUser, ProUser, TeamUser, ...
def make_new_user(user_class: Type[User]) -> User:
    # ...
    return user_class()

Type[C] 为协变量的意思是指, C 的所有子类都应使用与 C 相同的构造器签名及类方法签名。类型检查器应标记违反此项规定的内容,但也应允许符合指定基类构造器调用的子类进行构造器调用。PEP 484 修订版将来可能会调整类型检查器对这种特例的处理方式。

 Type 合法的参数仅有类、Any类型变量 以及上述类型的联合类型。例如:

def new_non_team_user(user_class: Type[BasicUser | ProUser]): ...

 Type[Any] 等价于 Type,进而等价于 Python 元类架构的根基,type

3.5.2 新版功能.

3.9 版后已移除: builtins.type 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

typing.Literal

表示类型检查器对应变量或函数参数的值等价于给定字面量(或多个字面量之一)的类型。例如:

def validate_simple(data: Any) -> Literal[True]:  # always returns True
    ...

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

PEP 593灵活函数和变量注解)里引入的类型,可以用上下文特定元数据(Annotated 的参数可变,也可能用它的多个组成部分)装饰现有的类型。具体来说,就是类型提示 Annotated[T, x] 用元数据 x 注解类型 T。静态分析或运行时都能使用该元数据。库(或工具)处理类型提示 Annotated[T, x] 时,在元数据 x 不涉及特殊逻辑的情况下,可忽略该类型提示,仅把它当作类型 T。与 typing 模块中现有的 no_type_check 功能不同,该功能完全禁用了函数或类的类型检查注解,而 Annotated 类型则允许对 T 进行静态类型检查(可安全地忽略 x),也可以在特定应用程序中实现 x 的运行时访问。

毕竟,如何解释注解(如有)由处理 Annotated 类型的工具/库负责。工具/库处理 Annotated 类型时,扫描所有注解以确定是否需要进行处理(例如,使用 isinstance())。

工具/库不支持注解,或遇到未知注解时,应忽略注解,并把注解类型当作底层类型。

是否允许客户端在一个类型上使用多个注解,以及如何合并这些注解,由处理注解的工具决定。

Annotated 类型支持把多个相同(或不同)的单个(或多个)类型注解置于任意节点。因此,使用这些注解的工具/库要负责处理潜在的重复项。例如,执行值范围分析时,应允许以下操作:

T1 = Annotated[int, ValueRange(-10, 5)]
T2 = Annotated[T1, ValueRange(-20, 3)]

传递 include_extras=Trueget_type_hints() ,即可在运行时访问额外的注解。

语义详情:

  • Annotated 的第一个参数必须是有效的类型。

  • 支持多个类型标注(Annotated 支持可变参数):

    Annotated[int, ValueRange(3, 10), ctype("char")]
    
  • 调用 Annotated 至少要有两个参数(Annotated[int] 是无效的)

  • 注解的顺序会被保留,且影响等价检查:

    Annotated[int, ValueRange(3, 10), ctype("char")] != Annotated[
        int, ctype("char"), ValueRange(3, 10)
    ]
    
  • 嵌套 Annotated 类型会被展平,元数据从最内层注解依序展开:

    Annotated[Annotated[int, ValueRange(3, 10)], ctype("char")] == Annotated[
        int, ValueRange(3, 10), ctype("char")
    ]
    
  • 不移除注解重复项:

    Annotated[int, ValueRange(3, 10)] != Annotated[
        int, ValueRange(3, 10), ValueRange(3, 10)
    ]
    
  • Annotated 可用于嵌套或泛型别名:

    T = TypeVar('T')
    Vec = Annotated[list[tuple[T, T]], MaxLen(10)]
    V = Vec[int]
    
    V == Annotated[list[tuple[int, int]], MaxLen(10)]
    

3.9 新版功能.

typing.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 新版功能.

构建泛型类型

以下内容是创建泛型类型的基石,但不在注解内使用。

class typing.Generic

用于泛型类型的抽象基类。

泛型类型一般通过继承含一个或多个类型变量的类实例进行声明。例如,泛型映射类型定义如下:

class Mapping(Generic[KT, VT]):
    def __getitem__(self, key: KT) -> VT:
        ...
        # Etc.

该类的用法如下:

X = TypeVar('X')
Y = TypeVar('Y')

def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y:
    try:
        return mapping[key]
    except KeyError:
        return default
class typing.TypeVar

类型变量。

用法:

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(x: T, n: int) -> Sequence[T]:
    """Return a list containing n references to x."""
    return [x]*n


def print_capitalized(x: S) -> S:
    """Print x capitalized, and return x."""
    print(x.capitalize())
    return x


def concatenate(x: A, y: A) -> A:
    """Add two strings or bytes objects together."""
    return x + y

请注意,类型变量可以是 被绑定的被约束的 ,或者两者都不是,但不能既是被绑定的 又是 被约束的。

约束类型变量和绑定类型变量在几个重要方面具有不同的语义。使用 约束 类型变量意味着它只能被解析为正好是所给的约束之一:

a = concatenate('one', 'two')  # Ok, variable 'a' has type 'str'
b = concatenate(StringSubclass('one'), StringSubclass('two'))  # Inferred type of variable 'b' 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

然而,使用一个 绑定 类型变量,意味着将使用最具体的类型来解析 TypeVar

print_capitalized('a string')  # Ok, output has type 'str'

class StringSubclass(str):
    pass

print_capitalized(StringSubclass('another string'))  # Ok, output has type 'StringSubclass'
print_capitalized(45)  # error: int is not a subtype of str

类型变量可以被绑定到具体类型、抽象类型( ABC 或 protocol ),甚至是类型的联合:

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

Bound type variables are particularly useful for annotating classmethods that serve as alternative constructors. In the following example (by Raymond Hettinger), the type variable C is bound to the Circle class through the use of a forward reference. Using this type variable to annotate the with_circumference classmethod, rather than hardcoding the return type as Circle, means that a type checker can correctly infer the return type even if the method is called on a subclass:

import math

C = TypeVar('C', bound='Circle')

class Circle:
    """An abstract circle"""

    def __init__(self, radius: float) -> None:
        self.radius = radius

    # Use a type variable to show that the return type
    # will always be an instance of whatever ``cls`` is
    @classmethod
    def with_circumference(cls: type[C], circumference: float) -> C:
        """Create a circle with the specified circumference"""
        radius = circumference / (math.pi * 2)
        return cls(radius)


class Tire(Circle):
    """A specialised circle (made out of rubber)"""

    MATERIAL = 'rubber'


c = Circle.with_circumference(3)  # Ok, variable 'c' has type 'Circle'
t = Tire.with_circumference(4)  # Ok, variable 't' has type 'Tire' (not 'Circle')

在运行时,isinstance(x, T) 会触发 TypeError 异常。一般而言,isinstance()issubclass() 不应与类型搭配使用。

类型变量可以通过传递 covariant=Truecontravariant=True 来标记协变或逆变。 更多细节请参见 PEP 484 。 默认情况下,类型变量是不变的。

class typing.ParamSpec(name, *, bound=None, covariant=False, contravariant=False)

参数规范变量。 类型变量 的一个专门版本。

用法:

P = ParamSpec('P')

参数规范变量的存在主要是为了使静态类型检查器受益。 它们被用来将一个可调用对象的参数类型转发给另一个可调用对象的参数类型——这种模式通常出现在高阶函数和装饰器中。 它们只有在 Concatenate 中使用时才有效,或者作为 Callable 的第一个参数,或者作为用户定义的泛型的参数。 参见 Generic 以了解更多关于泛型的信息。

例如,为了给一个函数添加基本的日志记录,我们可以创建一个装饰器 add_logging 来记录函数调用。 参数规范变量告诉类型检查器,传入装饰器的可调用对象和由其返回的新可调用对象有相互依赖的类型参数:

from collections.abc import Callable
from typing import TypeVar, ParamSpec
import logging

T = TypeVar('T')
P = ParamSpec('P')

def add_logging(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 的实例。

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

3.10 新版功能.

注解

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

参见

typing.ParamSpecArgs
typing.ParamSpecKwargs

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

在这些对象中的任何一个上调用 get_origin(),都会返回原始的 ParamSpec:

P = ParamSpec("P")
get_origin(P.args)  # returns P
get_origin(P.kwargs)  # returns P

3.10 新版功能.

typing.AnyStr

AnyStr 定义为 AnyStr = TypeVar('AnyStr', str, bytes)约束类型变量

这里指的是,它可以接受任意同类字符串,但不支持混用不同类别的字符串。例如:

def concat(a: AnyStr, b: AnyStr) -> AnyStr:
    return a + b

concat(u"foo", u"bar")  # Ok, output has type 'unicode'
concat(b"foo", b"bar")  # Ok, output has type 'bytes'
concat(u"foo", b"bar")  # Error, cannot mix unicode and bytes
class typing.Protocol(Generic)

Protocol 类的基类。Protocol 类的定义如下:

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(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() will check only the presence of the required methods or attributes, not their type signatures or types. For example, ssl.SSLObject is a class, therefore it passes an issubclass() check against Callable. However, the ssl.SSLObject.__init__ method exists only to raise a TypeError with a more informative message, therefore making it impossible to call (instantiate) ssl.SSLObject.

注解

针对运行时可检查协议的 isinstance() 检查相比针对非协议类的 isinstance() 检查可能会惊人的缓慢。 请考虑在性能敏感的代码中使用替代性写法如 hasattr() 调用进行结构检查。

3.8 新版功能.

其他特殊指令

这些特殊指令是声明类型的基石,但不在注解内使用。

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}>'

反向兼容用法:

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__ 属性。

class typing.NewType(name, tp)

一个辅助类,用于向类型检查器指示一个不同的类型,见 NewType。在运行时,它返回一个对象,在调用时返回其参数。用法:

UserId = NewType('UserId', int)
first_user = UserId(1)

3.5.2 新版功能.

在 3.10 版更改: NewType 现在是一个类而不是函数。

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 支持另外两种等价的语法形式:

Point2D = TypedDict('Point2D', x=int, y=int, label=str)
Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})

当任何一个键不是有效的 标识符 时,例如因为它们是关键字或包含连字符,也应该使用函数式语法。例子:

# 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 中。 可以通过指定总数来重写这一点。用法:

class Point2D(TypedDict, total=False):
    x: int
    y: int

这意味着一个 Point2D TypedDict 可以省略任何一个键。 类型检查器只需要支持一个字面的 FalseTrue 作为 total 参数的值。 True 是默认的,它使类主体中定义的所有项目都是必需的。

一个 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

T = TypeVar('T')
class XT(X, Generic[T]): pass  # raises TypeError

TypedDict 可以通过注解字典(参见 注解最佳实践 了解更多关于注解的最佳实践)、 __total____required_keys____optional_keys__ 进行内省。

__total__

Point2D.__total__ gives the value of the total argument. Example:

>>> 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
__required_keys__

3.9 新版功能.

__optional_keys__

Point2D.__required_keys__Point2D.__optional_keys__ 分别返回 frozenset 对象,其中包含必需和非必需键。目前,在同一个 中声明必需和非必需键的唯一方法是混合继承,用一个 total 参数值声明一个 TypedDict ,然后从另一个 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 新版功能.

更多示例与 TypedDict 的详细规则,详见 PEP 589

3.8 新版功能.

泛型具象容器

对应的内置类型

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

dict 的泛型版本。适用于注解返回类型。注解参数时,最好使用 Mapping 等抽象容器类型。

该类型用法如下:

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

3.9 版后已移除: builtins.dict 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

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

list 的泛型版本。适用于注解返回类型。注解参数时,最好使用 SequenceIterable 等抽象容器类型。

该类型用法如下:

T = TypeVar('T', int, float)

def vec2(x: T, y: T) -> List[T]:
    return [x, y]

def keep_positives(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 等抽象容器类型。

3.9 版后已移除: builtins.set 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

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

builtins.frozenset 的泛型版本。

3.9 版后已移除: builtins.frozenset 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

注解

Tuple 是一种特殊形式。

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.5.4 新版功能.

3.6.1 新版功能.

3.9 版后已移除: collections.ChainMap 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

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

collections.Counter 的泛型版本。

3.5.4 新版功能.

3.6.1 新版功能.

3.9 版后已移除: collections.Counter 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

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

collections.deque 的泛型版本。

3.5.4 新版功能.

3.6.1 新版功能.

3.9 版后已移除: collections.deque 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

其他具象类型

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

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

从 3.8 版起不建议使用,将在 3.13 版中移除: typing.io 命名空间被弃用并将被删除。 这些类型应该被直接从 typing 导入。

class typing.Pattern
class typing.Match

这些类型对应的是从 re.compile()re.match() 返回的类型。 这些类型(及相应的函数)是 AnyStr 中的泛型并可通过编写 Pattern[str], Pattern[bytes], Match[str]Match[bytes] 来具体指定。

从 3.8 版起不建议使用,将在 3.13 版中移除: typing.re 命名空间被弃用并将被删除。 这些类型应该被直接从 typing 导入。

3.9 版后已移除: re 模块中的 PatternMatch 类现已支持 []。详见 PEP 585GenericAlias 类型

class typing.Text

Textstr 的别名。提供了对 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 新版功能.

抽象基类

collections.abc 对应的容器

class typing.AbstractSet(Collection[T_co])

collections.abc.Set 的泛型版本。

3.9 版后已移除: collections.abc.Set 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.ByteString(Sequence[int])

collections.abc.ByteString 的泛型版本。

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

作为该类型的简称,bytes 可用于标注上述任意类型的参数。

3.9 版后已移除: collections.abc.ByteString now supports subscripting ([]). See PEP 585 and GenericAlias 类型.

class typing.Collection(Sized, Iterable[T_co], Container[T_co])

collections.abc.Collection 的泛型版本。

3.6.0 新版功能.

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 对应的其他类型

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

class typing.Generator(Iterator[T_co], Generic[T_co, T_contra, V_co])

生成器可以由泛型类型 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 的别名。

class typing.Reversible(Iterable[T_co])

collections.abc.Reversible 的泛型版本。

3.9 版后已移除: collections.abc.Reversible 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.Sized

collections.abc.Sized 的别名。

异步编程

class typing.Coroutine(Awaitable[V_co], Generic[T_co, T_contra, V_co])

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[T_co], Generic[T_co, T_contra])

异步生成器可由泛型类型 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 类型

上下文管理器类型

class typing.ContextManager(Generic[T_co])

contextlib.AbstractContextManager 的泛型版本。

3.5.4 新版功能.

3.6.0 新版功能.

3.9 版后已移除: contextlib.AbstractContextManager 现在支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

class typing.AsyncContextManager(Generic[T_co])

contextlib.AbstractAsyncContextManager 的泛型版本。

3.5.4 新版功能.

3.6.2 新版功能.

3.9 版后已移除: contextlib.AbstractAsyncContextManager 现在 支持下标操作 ([])。 参见 PEP 585GenericAlias 类型

协议

这些协议由 runtime_checkable() 装饰。

class typing.SupportsAbs

一个抽象基类,含一个抽象方法 __abs__,该方法与其返回类型协变。

class typing.SupportsBytes

一个抽象基类,含一个抽象方法 __bytes__

class typing.SupportsComplex

一个抽象基类,含一个抽象方法 __complex__

class typing.SupportsFloat

一个抽象基类,含一个抽象方法 __float__

class typing.SupportsIndex

一个抽象基类,含一个抽象方法 __index__

3.8 新版功能.

class typing.SupportsInt

一个抽象基类,含一个抽象方法 __int__

class typing.SupportsRound

一个抽象基类,含一个抽象方法 __round__,该方法与其返回类型协变。

函数与装饰器

typing.cast(typ, val)

把一个值转换为指定的类型。

这会把值原样返回。对类型检查器而言这代表了返回值具有指定的类型,在运行时我们故意没有设计任何检查(我们希望让这尽量快)。

@typing.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>

请参阅 PEP 484 了解更多细节以及与其他类型语义的比较。

@typing.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 新版功能.

@typing.no_type_check

标明注解不是类型提示的装饰器。

用作类或函数的 decorator。用于类时,递归地应用于该类中定义的所有方法,(但不影响超类或子类中定义的方法)。

本方法可直接修改函数。

@typing.no_type_check_decorator

让其他装饰器具有 no_type_check() 效果的装饰器。

本装饰器用 no_type_check() 里的装饰函数打包其他装饰器。

@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__ 相同。此外,可通过在 globalslocals 命名空间里进行评估,以此来处理编码为字符串字面量的前向引用。如有需要,在默认值设置为 None 时,可为函数或方法注解添加 Optional[t]。对于类 C,则返回由所有 __annotations__C.__mro__ 逆序合并而成的字典。

本函数以递归地方式用 T 替换所有 Annotated[T, ...], 除非将 include_extras 的值设置为 True (详见 Annotated)。例如:

class Student(NamedTuple):
    name: Annotated[str, 'some marker']

get_type_hints(Student) == {'name': str}
get_type_hints(Student, include_extras=False) == {'name': str}
get_type_hints(Student, include_extras=True) == {
    'name': Annotated[str, 'some marker']
}

注解

get_type_hints() 在导入的 类型别名 中不工作,包括前向引用。启用注解的延迟评估( PEP 563 )可能会消除对大多数前向引用的需要。

在 3.9 版更改: PEP 593 的组成部分,添加了 include_extras 参数。

typing.get_args(tp)
typing.get_origin(tp)

为泛型类型与特殊类型形式提供了基本的内省功能。

对于 X[Y, Z, ...] 形式的类型对象,这些函数返回 X(Y, Z, ...)。如果 X 是内置对象或 collections class 的泛型别名, 会将其标准化为原始类。如果 X 是包含在其他泛型类型中的联合类型或 Literal(Y, Z, ...) 的顺序会因类型缓存,而与原始参数 [Y, Z, ...] 的顺序不同。对于不支持的对象会相应地返回 None()。例如:

assert get_origin(Dict[str, int]) is dict
assert get_args(Dict[int, str]) == (int, str)

assert get_origin(Union[int, str]) is Union
assert get_args(Union[int, str]) == (int, str)

3.8 新版功能.

typing.is_typeddict(tp)

检查一个类型是否为 TypedDict

例如:

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

is_typeddict(Film)  # => True
is_typeddict(list | str)  # => False

3.10 新版功能.

class typing.ForwardRef

用于字符串前向引用的内部类型表示的类。 例如,List["SomeClass"] 会被隐式转换为 List[ForwardRef("SomeClass")]。 这个类不应由用户来实例化,但可以由内省工具使用。

注解

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 新版功能.