typing —- 类型提示支持

3.5 新版功能.

源码: Lib/typing.py

注解

Python 运行时不强制执行函数和变量类型注解,但这些注解可用于类型检查器、IDE、静态检查器等第三方工具。


本模块为 PEP 484PEP 526PEP 544PEP 586PEP 589PEP 591 中的类型提示提供运行时支持。AnyUnionTupleCallableTypeVarGeneric 等类型是最基础的组件。完整说明详见 PEP 484,类型提示简介请参阅 PEP 483

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

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

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

类型别名

把类型赋给别名,就可以定义类型别名。本例中,Vectorlist[float] 相同,可互换:

  1. Vector = list[float]
  2. def scale(scalar: float, vector: Vector) -> Vector:
  3. return [scalar * num for num in vector]
  4. # typechecks; a list of floats qualifies as a Vector.
  5. new_vector = scale(2.0, [1.0, -4.2, 5.4])

类型别名适用于简化复杂的类型签名。例如:

  1. from collections.abc import Sequence
  2. ConnectionOptions = dict[str, str]
  3. Address = tuple[str, int]
  4. Server = tuple[Address, ConnectionOptions]
  5. def broadcast_message(message: str, servers: Sequence[Server]) -> None:
  6. ...
  7. # The static type checker will treat the previous type signature as
  8. # being exactly equivalent to this one.
  9. def broadcast_message(
  10. message: str,
  11. servers: Sequence[tuple[tuple[str, int], dict[str, str]]]) -> None:
  12. ...

注意,None 是一种类型提示特例,已被 type(None) 取代。

NewType

NewType() 辅助函数可创建不同的新类型:

  1. from typing import NewType
  2. UserId = NewType('UserId', int)
  3. some_id = UserId(524313)

静态类型检查器把新类型当作原始类型的子类,这种方式适用于捕捉逻辑错误:

  1. def get_user_name(user_id: UserId) -> str:
  2. ...
  3. # typechecks
  4. user_a = get_user_name(UserId(42351))
  5. # does not typecheck; an int is not a UserId
  6. user_b = get_user_name(-1)

UserId 类型的变量可执行所有 int 操作,但返回结果都是 int 类型。这种方式允许在预期 int 时传入 UserId,还能防止意外创建无效的 UserId

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

注意,只有静态类型检查器强制执行这些检查。在运行时,Derived = NewType('Derived', Base) 语句把 Derived 当作函数,该函数直接返回传递给它的任何参数。即,Derived(some_value) 表达式不创建新类,或引入超出调用常规函数的开销。

更确切地说,在运行时,some_value is Derived(some_value) 表达式总为 True。

也就是说,不能创建 Derived 的子类型,因为,在运行时,它是标识函数,不是真正的类型:

  1. from typing import NewType
  2. UserId = NewType('UserId', int)
  3. # Fails at runtime and does not typecheck
  4. class AdminUserId(UserId): pass

但是,可以基于 ‘派生的’ NewType 创建 NewType()

  1. from typing import NewType
  2. UserId = NewType('UserId', int)
  3. ProUserId = NewType('ProUserId', UserId)

同时,ProUserId 的类型检查也可以按预期执行。

详见 PEP 484

注解

回顾上文,类型别名声明了两种彼此 等价 的类型。 Alias = Original 时,静态类型检查器认为 AliasOriginal 完全等价。 这种方式适用于简化复杂类型签名。

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

3.5.2 新版功能.

可调对象(Callable)

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

例如:

  1. from collections.abc import Callable
  2. def feeder(get_next_item: Callable[[], str]) -> None:
  3. # Body
  4. def async_query(on_success: Callable[[int], None],
  5. on_error: Callable[[int, Exception], None]) -> None:
  6. # Body

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

泛型(Generic)

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

  1. from collections.abc import Mapping, Sequence
  2. def notify_by_email(employees: Sequence[Employee],
  3. overrides: Mapping[str, str]) -> None: ...

typing 模块中新推出的 TypeVar 工厂函数实现泛型参数化。

  1. from collections.abc import Sequence
  2. from typing import TypeVar
  3. T = TypeVar('T') # Declare type variable
  4. def first(l: Sequence[T]) -> T: # Generic function
  5. return l[0]

用户定义的泛型类型

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

  1. from typing import TypeVar, Generic
  2. from logging import Logger
  3. T = TypeVar('T')
  4. class LoggedVar(Generic[T]):
  5. def __init__(self, value: T, name: str, logger: Logger) -> None:
  6. self.name = name
  7. self.logger = logger
  8. self.value = value
  9. def set(self, new: T) -> None:
  10. self.log('Set ' + repr(self.value))
  11. self.value = new
  12. def get(self) -> T:
  13. self.log('Get ' + repr(self.value))
  14. return self.value
  15. def log(self, message: str) -> None:
  16. self.logger.info('%s: %s', self.name, message)

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

Generic 基类定义了 __class_getitem__() ,因此,LoggedVar[t] 也是有效类型:

  1. from collections.abc import Iterable
  2. def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
  3. for var in vars:
  4. var.set(0)

泛型类型支持多个类型变量,不过,类型变量可能会受到限制:

  1. from typing import TypeVar, Generic
  2. ...
  3. T = TypeVar('T')
  4. S = TypeVar('S', int, str)
  5. class StrangePair(Generic[T, S]):
  6. ...

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

  1. from typing import TypeVar, Generic
  2. ...
  3. T = TypeVar('T')
  4. class Pair(Generic[T, T]): # INVALID
  5. ...

Generic 支持多重继承:

  1. from collections.abc import Sized
  2. from typing import TypeVar, Generic
  3. T = TypeVar('T')
  4. class LinkedList(Sized, Generic[T]):
  5. ...

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

  1. from collections.abc import Mapping
  2. from typing import TypeVar
  3. T = TypeVar('T')
  4. class MyDict(Mapping[str, T]):
  5. ...

比如,本例中 MyDict 调用的单参数,T

未指定泛型类的类型参数时,每个位置的类型都预设为 Any。下例中,MyIterable 不是泛型,但却隐式继承了 Iterable[Any]

  1. from collections.abc import Iterable
  2. class MyIterable(Iterable): # Same as Iterable[Any]

还支持用户定义的泛型类型别名。例如:

  1. from collections.abc import Iterable
  2. from typing import TypeVar, Union
  3. S = TypeVar('S')
  4. Response = Union[Iterable[S], int]
  5. # Return type here is same as Union[Iterable[str], int]
  6. def response(query: str) -> Response[str]:
  7. ...
  8. T = TypeVar('T', int, float, complex)
  9. Vec = Iterable[tuple[T, T]]
  10. def inproduct(v: Vec[T]) -> T: # Same as Iterable[tuple[T, T]]
  11. return sum(x*y for x, y in v)

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

抽象基类可作为用户定义的泛型类的基类,且不会与元类冲突。现已不再支持泛型元类。参数化泛型的输出结果会被缓存,typing 模块的大多数类型都可哈希、可进行等价对比。

Any 类型

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

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

  1. from typing import Any
  2. a = None # type: Any
  3. a = [] # OK
  4. a = 2 # OK
  5. s = '' # type: str
  6. s = a # OK
  7. def foo(item: Any) -> int:
  8. # Typechecks; 'item' could be any type,
  9. # and that type might have a 'bar' method
  10. item.bar()
  11. ...

注意,Any 类型的值赋给更精确的类型时,不执行类型检查。例如,把 a 赋给 s,在运行时,即便 s 已声明为 str 类型,但接收 int 值时,静态类型检查器也不会报错。

此外,未指定返回值与参数类型的函数,都隐式地默认使用 Any

  1. def legacy_parser(text):
  2. ...
  3. return data
  4. # A static type checker will treat the above
  5. # as having the same signature as:
  6. def legacy_parser(text: Any) -> Any:
  7. ...
  8. return data

需要混用动态与静态类型代码时,此操作把 Any 当作 应急出口

Anyobject 的区别。与 Any 相似,所有类型都是 object 的子类型。然而,与 Any 不同,object 不可逆:object 不是 其它类型的子类型。

就是说,值的类型是 object 时,类型检查器几乎会拒绝所有对它的操作,并且,把它赋给更精确的类型变量(或返回值)属于类型错误。例如:

  1. def hash_a(item: object) -> int:
  2. # Fails; an object does not have a 'magic' method.
  3. item.magic()
  4. ...
  5. def hash_b(item: Any) -> int:
  6. # Typechecks
  7. item.magic()
  8. ...
  9. # Typechecks, since ints and strs are subclasses of object
  10. hash_a(42)
  11. hash_a("foo")
  12. # Typechecks, since Any is compatible with all types
  13. hash_b(42)
  14. hash_b("foo")

使用 object,说明值能以类型安全的方式转为任何类型。使用 Any,说明值是动态类型。

名义子类型 vs 结构子类型

PEP 484 最初只是把 Python 静态类型系统定义为应用 名义子类型。即,当且仅当 AB 的子类时,才能在预期 B 类时应用 A 类。

此项要求以前也适用于抽象基类,例如,Iterable 。这种方式的问题在于,定义类时必须显式说明,既不 Pythonic,也不是动态类型式 Python 代码的惯用写法。例如,下列代码就遵从了 PEP 484 的规范:

  1. from collections.abc import Sized, Iterable, Iterator
  2. class Bucket(Sized, Iterable[int]):
  3. ...
  4. def __len__(self) -> int: ...
  5. def __iter__(self) -> Iterator[int]: ...

PEP 544 允许用户在类定义时不显式说明基类,从而解决了这一问题,静态类型检查器隐式认为 Bucket 既是 Sized 的子类型,又是 Iterable[int] 的子类型。这就是 结构子类型 (又称为静态鸭子类型):

  1. from collections.abc import Iterator, Iterable
  2. class Bucket: # Note: no base classes
  3. ...
  4. def __len__(self) -> int: ...
  5. def __iter__(self) -> Iterator[int]: ...
  6. def collect(items: Iterable[int]) -> int: ...
  7. 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

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

  1. from typing import NoReturn
  2. def stop() -> NoReturn:
  3. raise RuntimeError('no way')

3.5.4 新版功能.

3.6.2 新版功能.

特殊形式

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

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 585Generic Alias Type

typing.Union

联合类型;Union[X, Y] 的意思是,非 X 即 Y。

可用 Union[int, str] 等形式定义联合类型。 具体如下:

  • 参数必须是某种类型,且至少有一个。

  • 联合类型之联合类型会被展平,例如:

    1. Union[Union[int, str], float] == Union[int, str, float]
  • 单参数之联合类型就是该参数自身,例如:

    1. Union[int] == int # The constructor actually returns int
  • 冗余的参数会被跳过,例如:

    1. Union[int, str, int] == Union[int, str]
  • 比较联合类型,不涉及参数顺序,例如:

    1. Union[int, str] == Union[str, int]
  • 联合类型不能作为子类,也不能实例化。

  • 不支持 Union[X][Y] 这种写法。

  • Optional[X]Union[X, None] 的缩写。

在 3.7 版更改: 在运行时,不要移除联合类型中的显式子类。

typing.Optional

可选类型。

Optional[X] 等价于 Union[X, None]

注意,可选类型与含默认值的可选参数不同。含默认值的可选参数不需要在类型注解上添加 Optional 限定符,因为它仅是可选的。例如:

  1. def foo(arg: int = 0) -> None:
  2. ...

另一方面,显式应用 None 值时,不管该参数是否可选, Optional 都适用。例如:

  1. def foo(arg: Optional[int] = None) -> None:
  2. ...

typing.Callable

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

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

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

3.9 版后已移除: collections.abc.Callable 现已支持 []。 详见 PEP 585Generic Alias Type

class typing.Type(Generic[CT_co])

C 注解的变量可以接受类型 C 的值。反之,用 Type[C] 注解的变量可以接受类自身的值 — 准确地说,是接受 C类对象,例如:

  1. a = 3 # Has type 'int'
  2. b = int # Has type 'Type[int]'
  3. c = type(a) # Also has type 'Type[int]'

注意,Type[C] 为协变量:

  1. class User: ...
  2. class BasicUser(User): ...
  3. class ProUser(User): ...
  4. class TeamUser(User): ...
  5. # Accepts User, BasicUser, ProUser, TeamUser, ...
  6. def make_new_user(user_class: Type[User]) -> User:
  7. # ...
  8. return user_class()

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

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

  1. def new_non_team_user(user_class: Type[Union[BasicUser, ProUser]]): ...

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

3.5.2 新版功能.

3.9 版后已移除: builtins.type 现已支持 []。详见 PEP 585Generic Alias Type

typing.Literal

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

  1. def validate_simple(data: Any) -> Literal[True]: # always returns True
  2. ...
  3. MODE = Literal['r', 'rb', 'w', 'wb']
  4. def open_helper(file: str, mode: MODE) -> str:
  5. ...
  6. open_helper('/some/path', 'r') # Passes type check
  7. 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 内的变量注解是指,给定属性应当用作类变量,而不应设置在类实例上。用法如下:

  1. class Starship:
  2. stats: ClassVar[dict[str, int]] = {} # class variable
  3. damage: int = 10 # instance variable

ClassVar 仅接受类型,也不能使用下标。

ClassVar 本身不是类,不应用于 isinstance()issubclass()ClassVar 不改变 Python 运行时行为,但可以用于第三方类型检查器。例如,类型检查器会认为以下代码有错:

  1. enterprise_d = Starship(3000)
  2. enterprise_d.stats = {} # Error, setting class variable on instance
  3. Starship.stats = {} # This is OK

3.5.3 新版功能.

typing.Final

告知类型检查器某名称不能再次赋值或在子类中重写的特殊类型构造器。例如:

  1. MAX_SIZE: Final = 9000
  2. MAX_SIZE += 1 # Error reported by type checker
  3. class Connection:
  4. TIMEOUT: Final[int] = 10
  5. class FastConnector(Connection):
  6. 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 进行静态类型检查(例如,通过 mypy 或 Pyre,可安全地忽略 x),也可以在特定应用程序中实现 x 的运行时访问。

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

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

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

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

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

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

语义详情:

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

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

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

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

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

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

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

    1. T = TypeVar('T')
    2. Vec = Annotated[list[tuple[T, T]], MaxLen(10)]
    3. V = Vec[int]
    4. V == Annotated[list[tuple[int, int]], MaxLen(10)]

3.9 新版功能.

构建泛型类型

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

class typing.Generic

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

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

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

该类的用法如下:

  1. X = TypeVar('X')
  2. Y = TypeVar('Y')
  3. def lookup_name(mapping: Mapping[X, Y], key: X, default: Y) -> Y:
  4. try:
  5. return mapping[key]
  6. except KeyError:
  7. return default

class typing.TypeVar

类型变量。

用法:

  1. T = TypeVar('T') # Can be anything
  2. A = TypeVar('A', str, bytes) # Must be str or bytes

类型变量主要是为静态类型检查器提供支持,用于泛型类型与泛型函数定义的参数。有关泛型类型,详见 Generic。泛型函数的写法如下:

  1. def repeat(x: T, n: int) -> Sequence[T]:
  2. """Return a list containing n references to x."""
  3. return [x]*n
  4. def longest(x: A, y: A) -> A:
  5. """Return the longest of two strings."""
  6. return x if len(x) >= len(y) else y

本质上,后例的签名重载了 (str, str) -> str(bytes, bytes) -> bytes。注意,参数是 str 子类的实例时,返回类型仍是纯 str

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

通过 covariant=Truecontravariant=True 可以把类型变量标记为协变量或逆变量。详见 PEP 484。默认情况下,类型变量是不变量。类型变量还可以用 bound=<type> 指定上限。这里的意思是,(显式或隐式地)取代类型变量的实际类型必须是限定类型的子类,详见 PEP 484

typing.AnyStr

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

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

  1. def concat(a: AnyStr, b: AnyStr) -> AnyStr:
  2. return a + b
  3. concat(u"foo", u"bar") # Ok, output has type 'unicode'
  4. concat(b"foo", b"bar") # Ok, output has type 'bytes'
  5. concat(u"foo", b"bar") # Error, cannot mix unicode and bytes

class typing.Protocol(Generic)

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

  1. class Proto(Protocol):
  2. def meth(self) -> int:
  3. ...

这些类主要与静态类型检查器搭配使用,用来识别结构子类型(静态鸭子类型),例如:

  1. class C:
  2. def meth(self) -> int:
  3. return 0
  4. def func(x: Proto) -> int:
  5. return x.meth()
  6. func(C()) # Passes static type check

详见 PEP 544。Protocol 类用 runtime_checkable() (见下文)装饰,忽略类型签名,仅检查给定属性是否存在,充当简要的运行时协议。

Protocol 类可以是泛型,例如:

  1. class GenProto(Protocol[T]):
  2. def meth(self) -> T:
  3. ...

3.8 新版功能.

@``typing.runtime_checkable

用于把 Protocol 类标记为运行时协议。

该协议可以与 isinstance()issubclass() 一起使用。应用于非协议的类时,会触发 TypeError。该指令支持简易结构检查,与 collections.abcIterable 非常类似,只擅长做一件事。 例如:

  1. @runtime_checkable
  2. class Closable(Protocol):
  3. def close(self): ...
  4. assert isinstance(open('/some/file'), Closable)

注解

runtime_checkable() 只检查所需方法是否存在,但却不检查类型签名! 例如,builtins.complex 支持 __float__(),因此,它能通过 SupportsFloatissubclass() 检查。 然而,complex.__float__ 方法其实只是为了触发含更多信息的 TypeError

3.8 新版功能.

其他特殊指令

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

class typing.NamedTuple

collections.namedtuple() 的类型版本。

用法:

  1. class Employee(NamedTuple):
  2. name: str
  3. id: int

相当于:

  1. Employee = collections.namedtuple('Employee', ['name', 'id'])

为字段提供默认值,要在类体内赋值:

  1. class Employee(NamedTuple):
  2. name: str
  3. id: int = 3
  4. employee = Employee('Guido')
  5. assert employee.id == 3

带默认值的字段必须在不带默认值的字段后面。

生成的类具有 __annotations__ 这个附加属性,提供了映射字段名与字段类型的字典。(字段名在 _fields 属性内,默认值在 _field_defaults 属性内,这两项都是命名元组 API 的组成部分。)

NamedTuple 子类也支持文档字符串与方法:

  1. class Employee(NamedTuple):
  2. """Represents an employee."""
  3. name: str
  4. id: int = 3
  5. def __repr__(self) -> str:
  6. return f'<Employee {self.name}, id={self.id}>'

反向兼容用法:

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

typing.NewType(name, tp)

用于为类型检查器标明不同类型的辅助函数,详见 NewType。在运行时,它返回一个返回其参数的函数。用法如下:

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

3.5.2 新版功能.

class typing.TypedDict(dict)

把类型提示添加至字典的特殊构造器。在运行时,它是纯 dict

TypedDict 声明一个字典类型,该类型预期所有实例都具有一组键集,其中,每个键都与对应类型的值关联。运行时不检查此预期,而是由类型检查器强制执行。用法如下:

  1. class Point2D(TypedDict):
  2. x: int
  3. y: int
  4. label: str
  5. a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK
  6. b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check
  7. assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')

用于内省的类型信息可通过 Point2D.__annotations__Point2D.__total__ 访问。为了让不支持 PEP 526 的老版 Python 也能使用此功能,TypedDict 支持两个附加的等价句法形式:

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

默认情况下,所有键都必须列在 TypedDict 里。不过,也可以通过指定 total 参数进行重写。用法如下:

  1. class point2D(TypedDict, total=False):
  2. x: int
  3. y: int

这段代码的意思是,可以省略 point2D 这个 TypedDict 中的任意键。类型检查器只支持字面量 False 或 True 作为 total 参数的值。True 是默认值,表明在类体中,必须定义所有项目。

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

3.8 新版功能.

泛型具象容器

对应的内置类型

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

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

该类型用法如下:

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

3.9 版后已移除: builtins.dict 现已支持 []。详见 PEP 585Generic Alias Type

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

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

该类型用法如下:

  1. T = TypeVar('T', int, float)
  2. def vec2(x: T, y: T) -> List[T]:
  3. return [x, y]
  4. def keep_positives(vector: Sequence[T]) -> List[T]:
  5. return [item for item in vector if item > 0]

3.9 版后已移除: builtins.list 现已支持 []。详见 PEP 585Generic Alias Type

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

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

3.9 版后已移除: builtins.set 现已支持 []。详见 PEP 585Generic Alias Type

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

builtins.frozenset 的泛型版本。

3.9 版后已移除: builtins.frozenset 现已支持 []。详见 PEP 585Generic Alias Type

注解

Tuple 是一种特殊形式。

collections 对应类型

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

collections.defaultdict 的泛型版本。

3.5.2 新版功能.

3.9 版后已移除: collections.defaultdict 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.OrderedDict 的泛型版本。

3.7.2 新版功能.

3.9 版后已移除: collections.OrderedDict 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.ChainMap 的泛型版本。

3.5.4 新版功能.

3.6.1 新版功能.

3.9 版后已移除: collections.ChainMap 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.Counter 的泛型版本。

3.5.4 新版功能.

3.6.1 新版功能.

3.9 版后已移除: collections.Counter 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.deque 的泛型版本。

3.5.4 新版功能.

3.6.1 新版功能.

3.9 版后已移除: collections.deque 现已支持 []。详见 PEP 585Generic Alias Type

其他具象类型

class typing.IO

class typing.TextIO

class typing.BinaryIO

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

Deprecated since version 3.8, will be removed in version 3.12: 这些类型也在 typing.io 命名空间中,它从未得到类型检查器的支持并将被移除。

class typing.Pattern

class typing.Match

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

Deprecated since version 3.8, will be removed in version 3.12: 这些类型也在 typing.re 命名空间中,它从未得到类型检查器的支持并将被移除。

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

class typing.Text

Textstr 的别名。提供了对 Python 2 代码的向下兼容:Python 2 中,Textunicode 的别名。

使用 Text 时,值中必须包含 unicode 字符串,以兼容 Python 2 和 Python 3:

  1. def add_unicode_checkmark(text: Text) -> Text:
  2. return text + u' \u2713'

3.5.2 新版功能.

抽象基类

collections.abc 对应的容器

class typing.AbstractSet(Sized, Collection[T_co])

collections.abc.Set 的泛型版本。

3.9 版后已移除: collections.abc.Set 现已支持 []。详见 PEP 585Generic Alias Type

class typing.ByteString(Sequence[int])

collections.abc.ByteString 的泛型版本。

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

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

3.9 版后已移除: collections.abc.ByteString 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.abc.Collection 的泛型版本。

3.6.0 新版功能.

3.9 版后已移除: collections.abc.Collection 现已支持 []。详见 PEP 585Generic Alias Type

class typing.Container(Generic[T_co])

collections.abc.Container 的泛型版本。

3.9 版后已移除: collections.abc.Container 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.abc.ItemsView 的泛型版本。

3.9 版后已移除: collections.abc.ItemsView 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.abc.KeysView 的泛型版本。

3.9 版后已移除: collections.abc.KeysView 现已支持 []。详见 PEP 585Generic Alias Type

class typing.Mapping(Sized, Collection[KT], Generic[VT_co])

collections.abc.Mapping 的泛型版本。用法如下:

  1. def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
  2. return word_list[word]

3.9 版后已移除: collections.abc.Mapping 现已支持 []。详见 PEP 585Generic Alias Type

class typing.MappingView(Sized, Iterable[T_co])

collections.abc.MappingView 的泛型版本。

3.9 版后已移除: collections.abc.MappingView 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.abc.MutableMapping 的泛型版本。

3.9 版后已移除: collections.abc.MutableMapping 现已支持 []。详见 PEP 585Generic Alias Type

class typing.MutableSequence(Sequence[T])

collections.abc.MutableSequence 的泛型版本。

3.9 版后已移除: collections.abc.MutableSequence 现已支持 []。详见 PEP 585Generic Alias Type

class typing.MutableSet(AbstractSet[T])

collections.abc.MutableSet 的泛型版本。

3.9 版后已移除: collections.abc.MutableSet 现已支持 []。详见 PEP 585Generic Alias Type

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

collections.abc.Sequence 的泛型版本。

3.9 版后已移除: collections.abc.Sequence 现已支持 []。详见 PEP 585Generic Alias Type

class typing.ValuesView(MappingView[VT_co])

collections.abc.ValuesView 的泛型版本。

3.9 版后已移除: collections.abc.ValuesView 现已支持 []。详见 PEP 585Generic Alias Type

collections.abc 对应的其他类型

class typing.Iterable(Generic[T_co])

collections.abc.Iterable 的泛型版本。

3.9 版后已移除: collections.abc.Iterable 现已支持 []。详见 PEP 585Generic Alias Type

class typing.Iterator(Iterable[T_co])

collections.abc.Iterator 的泛型版本。

3.9 版后已移除: collections.abc.Iterator 现已支持 []。详见 PEP 585Generic Alias Type

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

生成器可以由泛型类型 Generator[YieldType, SendType, ReturnType] 注解。例如:

  1. def echo_round() -> Generator[int, float, str]:
  2. sent = yield 0
  3. while sent >= 0:
  4. sent = yield round(sent)
  5. return 'Done'

注意,与 typing 模块里的其他泛型不同, GeneratorSendType 属于逆变行为,不是协变行为,也是不变行为。

如果生成器只产生值,可将 SendTypeReturnType 设为 None

  1. def infinite_stream(start: int) -> Generator[int, None, None]:
  2. while True:
  3. yield start
  4. start += 1

此外,还可以把生成器的返回类型注解为 Iterable[YieldType]Iterator[YieldType]

  1. def infinite_stream(start: int) -> Iterator[int]:
  2. while True:
  3. yield start
  4. start += 1

3.9 版后已移除: collections.abc.Generator 现已支持 []。详见 PEP 585Generic Alias Type

class typing.Hashable

collections.abc.Hashable 的别名。

class typing.Reversible(Iterable[T_co])

collections.abc.Reversible 的泛型版本。

3.9 版后已移除: collections.abc.Reversible 现已支持 []。详见 PEP 585Generic Alias Type

class typing.Sized

collections.abc.Sized 的别名。

异步编程

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

collections.abc.Coroutine 的泛型版本。类型变量的差异和顺序与 Generator 的内容相对应,例如:

  1. from collections.abc import Coroutine
  2. c = None # type: Coroutine[list[str], str, int]
  3. ...
  4. x = c.send('hi') # type: list[str]
  5. async def bar() -> None:
  6. x = await c # type: int

3.5.3 新版功能.

3.9 版后已移除: collections.abc.Coroutine 现已支持 []。详见 PEP 585Generic Alias Type

class typing.AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra])

异步生成器可由泛型类型 AsyncGenerator[YieldType, SendType] 注解。例如:

  1. async def echo_round() -> AsyncGenerator[int, float]:
  2. sent = yield 0
  3. while sent >= 0.0:
  4. rounded = await round(sent)
  5. sent = yield rounded

与常规生成器不同,异步生成器不能返回值,因此没有 ReturnType 类型参数。 与 Generator 类似,SendType 也属于逆变行为。

如果生成器只产生值,可将 SendType 设置为 None

  1. async def infinite_stream(start: int) -> AsyncGenerator[int, None]:
  2. while True:
  3. yield start
  4. start = await increment(start)

此外,可用 AsyncIterable[YieldType]AsyncIterator[YieldType] 注解生成器的返回类型:

  1. async def infinite_stream(start: int) -> AsyncIterator[int]:
  2. while True:
  3. yield start
  4. start = await increment(start)

3.6.1 新版功能.

3.9 版后已移除: collections.abc.AsyncGenerator 现已支持 []。详见 PEP 585Generic Alias Type

class typing.AsyncIterable(Generic[T_co])

collections.abc.AsyncIterable 的泛型版本。

3.5.2 新版功能.

3.9 版后已移除: collections.abc.AsyncIterable 现已支持 []。详见 PEP 585Generic Alias Type

class typing.AsyncIterator(AsyncIterable[T_co])

collections.abc.AsyncIterator 的泛型版本。

3.5.2 新版功能.

3.9 版后已移除: collections.abc.AsyncIterator 现已支持 []。详见 PEP 585Generic Alias Type

class typing.Awaitable(Generic[T_co])

collections.abc.Awaitable 的泛型版本。

3.5.2 新版功能.

3.9 版后已移除: collections.abc.Awaitable 现已支持 []。详见 PEP 585Generic Alias Type

上下文管理器类型

class typing.ContextManager(Generic[T_co])

contextlib.AbstractContextManager 的泛型版本。

3.5.4 新版功能.

3.6.0 新版功能.

3.9 版后已移除: contextlib.AbstractContextManager 现已支持 []。详见 PEP 585Generic Alias Type

class typing.AsyncContextManager(Generic[T_co])

contextlib.AbstractAsyncContextManager 的泛型版本。

3.5.4 新版功能.

3.6.2 新版功能.

3.9 版后已移除: contextlib.AbstractAsyncContextManager 现已支持 []。详见 PEP 585Generic Alias Type

协议

这些协议由 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。下面的重载示例给出了比联合类型或类型变量更精准的类型:

  1. @overload
  2. def process(response: None) -> None:
  3. ...
  4. @overload
  5. def process(response: int) -> tuple[int, str]:
  6. ...
  7. @overload
  8. def process(response: bytes) -> str:
  9. ...
  10. def process(response):
  11. <actual implementation>

详见 PEP 484,与其他类型语义进行对比。

@``typing.final

告知类型检查器被装饰的方法不能被覆盖,且被装饰的类不能作为子类的装饰器,例如:

  1. class Base:
  2. @final
  3. def done(self) -> None:
  4. ...
  5. class Sub(Base):
  6. def done(self) -> None: # Error reported by type checker
  7. ...
  8. @final
  9. class Leaf:
  10. ...
  11. class Other(Leaf): # Error reported by type checker
  12. ...

这些属性没有运行时检查。详见 PEP 591

3.8 新版功能.

@``typing.no_type_check

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

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

本方法可直接修改函数。

@``typing.no_type_check_decorator

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

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

@``typing.type_check_only

标记类或函数内不可用于运行时的装饰器。

在运行时,该装饰器本身不可用。实现返回的是私有类实例时,它主要是用于标记在类型存根文件中定义的类。

  1. @type_check_only
  2. class Response: # private or not available at runtime
  3. code: int
  4. def get_header(self, name: str) -> str: ...
  5. 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)。例如:

  1. class Student(NamedTuple):
  2. name: Annotated[str, 'some marker']
  3. get_type_hints(Student) == {'name': str}
  4. get_type_hints(Student, include_extras=False) == {'name': str}
  5. get_type_hints(Student, include_extras=True) == {
  6. 'name': Annotated[str, 'some marker']
  7. }

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

typing.get_args(tp)

typing.get_origin(tp)

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

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

  1. assert get_origin(Dict[str, int]) is dict
  2. assert get_args(Dict[int, str]) == (int, str)
  3. assert get_origin(Union[int, str]) is Union
  4. assert get_args(Union[int, str]) == (int, str)

3.8 新版功能.

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。用法如下:

  1. if TYPE_CHECKING:
  2. import expensive_mod
  3. def fun(arg: 'expensive_mod.SomeType') -> None:
  4. local_var: expensive_mod.AnotherType = other_fun()

第一个类型注解必须用引号标注,才能把它当作“前向引用”,从而在解释器运行时中隐藏 expensive_mod 引用。局部变量的类型注释不会被评估,因此,第二个注解不需要用引号引起来。

注解

Python 3.7 或更高版本中使用 from __future__ import 时,函数定义时不处理注解, 而是把注解当作字符串存在 __annotations__ 里,这样就不必为注解使用引号。(详见 PEP 563)。

3.5.2 新版功能.