Skip to main content

26.1. typing —支持类型提示

3.5 新版功能.

源代码: Lib/typing.py


此模块支持由 PEP 484PEP 526 指定的类型提示。最基本的支持包括类型 AnyUnionTupleCallableTypeVarGeneric。有关完整规格,请参阅 PEP 484。有关类型提示的简化介绍,请参阅 PEP 483

下面的函数接受并返回一个字符串,并注释如下:

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

在函数 greeting 中,参数 name 预期为 str 类型,返回类型为 str。接受子类型作为参数。

26.1.1. 类型别名

通过将类型分配给别名来定义类型别名。在本示例中,VectorList[float] 将被视为可互换的同义词:

from typing import List
Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

# typechecks; a list of floats qualifies as a Vector.
new_vector = scale(2.0, [1.0, -4.2, 5.4])

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

from typing import Dict, Tuple, List

ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

def broadcast_message(message: str, servers: List[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: List[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
    ...

注意,None 作为类型提示是一种特殊情况,并由 type(None) 替代。

26.1.2. NewType

使用 NewType() 辅助函数创建不同类型:

from typing import NewType

UserId = NewType('UserId', int)
some_id = UserId(524313)

静态类型检查器将把新类型视为原始类型的子类。这有助于捕获逻辑错误:

def get_user_name(user_id: UserId) -> str:
    ...

# typechecks
user_a = get_user_name(UserId(42351))

# does not typecheck; an int is not a UserId
user_b = get_user_name(-1)

您仍然可以对类型为 UserId 的变量执行所有 int 操作,但结果将始终为类型 int。这允许您传递 UserId 在任何地方可能期望 int,但将阻止您以无效方式意外创建 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 的子类型,因为它是运行时的标识函数,而不是实际类型。类似地,不可能基于 Derived 类型创建另一 NewType():

from typing import NewType

UserId = NewType('UserId', int)

# Fails at runtime and does not typecheck
class AdminUserId(UserId): pass

# Also does not typecheck
ProUserId = NewType('ProUserId', UserId)

有关详细信息,请参阅 PEP 484

注解

回想一下,类型别名的使用声明两种类型是彼此的 equivalent。在所有情况下,执行 Alias = Original 将使静态类型检查器将 Alias 视为 完全等同Original。当您想要简化复杂类型签名时,这是有用的。

相比之下,NewType 声明一种类型是另一种的 subtype。执行 Derived = NewType('Derived', Original) 将使静态类型检查器将 Derived 视为 Originalsubclass,这意味着类型 Original 的值不能在期望类型 Derived 的值的地方使用。当您希望以最小的运行时成本防止逻辑错误时,这是有用的。

26.1.3. 可叫

期望特定签名的回调函数的框架可能使用 Callable[[Arg1Type, Arg2Type], ReturnType] 类型提示。

例如:

from typing 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

可以通过用类型hint:Callable[..., ReturnType] 中的参数列表替换文本省略号来声明可调用的返回类型,而不指定调用签名。

26.1.4. 泛型

由于不能以通用方式静态推断容器中保存的对象的类型信息,因此已经扩展了抽象基类以支持预订来表示容器元素的预期类型。

from typing import Mapping, Sequence

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

通过使用在名为 TypeVar 的打字中可用的新工厂,可以对通用参数进行参数化。

from typing import Sequence, TypeVar

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

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

26.1.5. 用户定义的泛型类型

用户定义的类可以定义为通用类。

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 有效作为类体中的类型。

Generic 基类使用定义 __getitem__() 的元类,以便 LoggedVar[t] 作为类型有效:

from typing import Iterable

def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
    for var in vars:
        var.set(0)

通用类型可以具有任意数量的类型变量,并且类型变量可以被约束:

from typing import TypeVar, Generic
...

T = TypeVar('T')
S = TypeVar('S', int, str)

class StrangePair(Generic[T, S]):
    ...

Generic 的每个类型变量参数必须是不同的。因此这是无效的:

from typing import TypeVar, Generic
...

T = TypeVar('T')

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

您可以使用 Generic 的多重继承:

from typing import TypeVar, Generic, Sized

T = TypeVar('T')

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

当从泛型类继承时,一些类型变量可以被修复:

from typing import TypeVar, Mapping

T = TypeVar('T')

class MyDict(Mapping[str, T]):
    ...

在这种情况下,MyDict 具有单个参数 T

使用不指定类型参数的泛型类为每个位置假定 Any。在以下示例中,MyIterable 不是通用的,而是隐式地继承自 Iterable[Any]:

from typing import Iterable

class MyIterable(Iterable): # Same as Iterable[Any]

还支持用户定义的通用类型别名。例子:

from typing import TypeVar, Iterable, Tuple, Union
S = TypeVar('S')
Response = Union[Iterable[S], int]

# Return type here is same as Union[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)

Generic 使用的元类是 abc.ABCMeta 的子类。通用类可以是通过包括抽象方法或属性的ABC,并且通用类也可以具有ABC作为基类而没有元类冲突。不支持通用元类。参数化泛型的结果被缓存,并且在类型模块中的大多数类型是可哈希的并且可比较的相等。

26.1.6. Any 类型

一种特殊类型的是 Any。静态类型检查器将把每种类型视为与 AnyAny 兼容,以与每种类型兼容。

这意味着可以对 Any 上的类型的值执行任何操作或方法调用,并将其分配给任何变量:

from typing import Any

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

s = ''      # type: str
s = a       # OK

def foo(item: Any) -> int:
    # Typechecks; '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 不同,反之不是真的:objectnot 每个其他类型的子类型。

这意味着当值的类型是 object 时,类型检查器将拒绝几乎所有操作,并将其分配给更专门类型的变量(或使用它作为返回值)是类型错误。例如:

def hash_a(item: object) -> int:
    # Fails; an object does not have a 'magic' method.
    item.magic()
    ...

def hash_b(item: Any) -> int:
    # Typechecks
    item.magic()
    ...

# Typechecks, since ints and strs are subclasses of object
hash_a(42)
hash_a("foo")

# Typechecks, since Any is compatible with all types
hash_b(42)
hash_b("foo")

使用 object 以类型安全的方式指示值可以是任何类型。使用 Any 表示值是动态类型。

26.1.7. 类,函数和装饰器

模块定义了以下类,函数和装饰器:

class typing.TypeVar

类型变量。

用法:

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

类型变量主要用于静态类型检查器的好处。它们用作泛型类型以及通用函数定义的参数。有关泛型类型的更多信息,请参阅类Generic。通用函数的工作方式如下:

def repeat(x: T, n: int) -> Sequence[T]:
    """Return a list containing n references to x."""
    return [x]*n

def longest(x: A, y: A) -> A:
    """Return the longest of two strings."""
    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

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.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[Union[BaseUser, ProUser]]): ...

Type[Any] 等价于 TypeType 又等同于 type,它是Python的元类层次结构的根。

class typing.Iterable(Generic[T_co])

collections.abc.Iterable 的通用版本。

class typing.Iterator(Iterable[T_co])

collections.abc.Iterator 的通用版本。

class typing.Reversible(Iterable[T_co])

collections.abc.Reversible 的通用版本。

class typing.SupportsInt

一个ABC与一个抽象方法 __int__

class typing.SupportsFloat

一个ABC与一个抽象方法 __float__

class typing.SupportsAbs

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

class typing.SupportsRound

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

class typing.Container(Generic[T_co])

collections.abc.Container 的通用版本。

class typing.Hashable

collections.abc.Hashable 的别名

class typing.Sized

collections.abc.Sized 的别名

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

collections.abc.Collection 的通用版本

3.6 新版功能.

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

collections.abc.Set 的通用版本。

class typing.MutableSet(AbstractSet[T])

collections.abc.MutableSet 的通用版本。

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

collections.abc.Mapping 的通用版本。

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

collections.abc.MutableMapping 的通用版本。

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

collections.abc.Sequence 的通用版本。

class typing.MutableSequence(Sequence[T])

collections.abc.MutableSequence 的通用版本。

class typing.ByteString(Sequence[int])

collections.abc.ByteString 的通用版本。

此类型表示类型 bytesbytearraymemoryview

作为此类型的简写,bytes 可用于注释上述任何类型的参数。

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

list 的通用版本。用于注释返回类型。要注释参数,最好使用抽象集合类型,例如 MappingSequenceAbstractSet

这种类型可以如下使用:

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]
class typing.Set(set, MutableSet[T])

builtins.set 的通用版本。

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

builtins.frozenset 的通用版本。

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

collections.abc.MappingView 的通用版本。

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

collections.abc.KeysView 的通用版本。

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

collections.abc.ItemsView 的通用版本。

class typing.ValuesView(MappingView[VT_co])

collections.abc.ValuesView 的通用版本。

class typing.Awaitable(Generic[T_co])

collections.abc.Awaitable 的通用版本。

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

collections.abc.Coroutine 的通用版本。类型变量的方差和顺序对应于例如 Generator 的方差和顺序:

from typing import List, Coroutine
c = None # type: Coroutine[List[str], str, int]
...
x = c.send('hi') # type: List[str]
async def bar() -> None:
    x = await c # type: int
class typing.AsyncIterable(Generic[T_co])

collections.abc.AsyncIterable 的通用版本。

class typing.AsyncIterator(AsyncIterable[T_co])

collections.abc.AsyncIterator 的通用版本。

class typing.ContextManager(Generic[T_co])

contextlib.AbstractContextManager 的通用版本。

3.6 新版功能.

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

dict 的通用版本。这种类型的用法如下:

def get_position_in_index(word_list: Dict[str, int], word: str) -> int:
    return word_list[word]
class typing.DefaultDict(collections.defaultdict, MutableMapping[KT, VT])

collections.defaultdict 的通用版本

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'

注意,与打字模块中的许多其他泛型不同,GeneratorSendType 逆向变化,而不是协变或不变。

如果你的发电机只产生价值,设置 SendTypeReturnTypeNone:

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
class typing.Text

Textstr 的别名。它提供为Python 2代码提供一个向前兼容的路径:在Python 2中,Textunicode 的别名。

使用 Text 来指示值必须以与Python 2和Python 3兼容的方式包含unicode字符串:

def add_unicode_checkmark(text: Text) -> Text:
    return text + u' \u2713'
class typing.io

I/O流类型的包装器命名空间。

这定义了通用类型 IO[AnyStr],并且分别针对 IO[str]IO[bytes] 别名 TextIOBinaryIO。这些表示由 open() 返回的I/O流的类型。

class typing.re

正则表达式匹配类型的Wrapper命名空间。

这定义了对应于来自 re.compile()re.match() 的返回类型的类型别名 PatternMatch。这些类型(和相应的功能)在 AnyStr 中是通用的,并且可以通过写入 Pattern[str]Pattern[bytes]Match[str]Match[bytes] 来具体化。

class typing.NamedTuple

namedtuple的类型化版本。

用法:

class Employee(NamedTuple):
    name: str
    id: int

这相当于:

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

结果类有一个额外的属性:_field_types,给出一个dict将字段名称映射到类型。 (字段名称在 _fields 属性中,它是namedtuple API的一部分。)

向后兼容使用:

Employee = NamedTuple('Employee', [('name', str), ('id', int)])

在 3.6 版更改: 添加了对 PEP 526 变量注释语法的支持。

typing.NewType(typ)

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

UserId = NewType('UserId', int)
first_user = UserId(1)
typing.cast(typ, val)

将值转换为类型。

这将返回不变的值。对于类型检查器,这表示返回值具有指定的类型,但在运行时我们有意不检查任何东西(我们希望这是尽可能快的)。

typing.get_type_hints(obj[, globals[, locals]])

返回一个包含函数,方法,模块或类对象的类型提示的字典。

这通常与 obj.__annotations__ 相同。此外,编码为字符串字面量的前向引用通过在 globalslocals 命名空间中评估它们来处理。如果必要,如果设置了等于 None 的默认值,则为函数和方法注释添加 Optional[t]。对于类 C,返回通过沿着 C.__mro__ 以相反顺序合并所有 __annotations__ 构造的字典。

@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.no_type_check(arg)

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

参数必须是类或函数;如果它是一个类,它将递归应用于该类中定义的所有方法(但不适用于在其超类或子类中定义的方法)。

这会改变函数的位置。

@typing.no_type_check_decorator(decorator)

装饰师给另一个装饰师 no_type_check() 效果。

这将装饰器包装在 no_type_check() 中的装饰函数。

typing.Any

表示无约束类型的特殊类型。

  • 每种类型都与 Any 兼容。

  • Any 兼容各种类型。

typing.Union

联合型; Union[X, Y] 是指X或Y.

要定义联合,请使用 Union[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]
    
  • 当比较联合时,参数顺序被忽略,例如。:

    Union[int, str] == Union[str, int]
    
  • 当一个类及其子类存在时,前者被跳过,例如。:

    Union[int, object] == object
    
  • 您不能子类化或实例化union。

  • 你不能写 Union[X][Y]

  • 您可以使用 Optional[X] 作为 Union[X, None] 的缩写。

typing.Optional

可选类型。

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

请注意,这不是一个可选参数的概念,它是一个具有默认值的参数。具有默认值的可选参数不需要在其类型注释上使用 Optional 限定符(虽然如果默认值为 None,则推断)。如果允许 None 的显式值,则强制参数可以仍然具有 Optional 类型。

typing.Tuple

元组类型; Tuple[X, Y] 是具有类型X的第一项和类型Y的第二项的两个项目的元组的类型。

示例:Tuple[T1, T2] 是对应于类型变量T1和T2的两个元素的元组。 Tuple[int, float, str] 是一个int,一个float和一个字符串的元组。

要指定同构类型的可变长度元组,请使用文字省略号,例如。 Tuple[int, ...]。普通的 Tuple 相当于 Tuple[Any, ...],进而相当于 tuple

typing.Callable

可叫类型; Callable[[int], str] 是(int) - > str的函数。

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

没有用于指示可选或关键字参数的语法;这样的函数类型很少用作回调类型。 Callable[..., ReturnType] (文本省略号)可以用于键入提示,可调用任何数量的参数并返回 ReturnType。平原 Callable 等价于 Callable[..., Any],反过来等同于 collections.abc.Callable

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

由第三方静态类型检查器假定为 True 的特殊常量。它在运行时是 False。用法:

if TYPE_CHECKING:
    import expensive_mod

def fun():
    local_var: expensive_mod.some_type = other_fun()