26.1. typing —— 支持类型提示

版本3.5中的新功能。

源代码: Lib/typing.py

此模块支持由 PEP 484指定的类型提示。The most fundamental support consists of the type Any, Union, Tuple, Callable, TypeVar, and Generic. 有关完整规格,请参阅 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:
    ...

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类型。这样,您可以在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的子类型,因为它是运行时的标识函数,而不是实际类型。类似地,不可能基于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

注意

回想一下,类型别名的使用声明两种类型彼此等效执行别名 = 原始会使静态类型检查器将Alias视为Original当您想要简化复杂类型签名时,这是非常有用的。

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

26.1.3.可调用

期望特定签名的回调函数的框架可以使用Callable [[Arg1Type, Arg2Type],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中的参数列表替换字面值省略来声明可调用的返回类型,而不指定调用声明:hint:Callable [..., ReturnType]None作为类型提示是一种特殊情况,并替换为type(None)

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('{}: {}'.format(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]

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的值指定为更精确的类型时,不会执行类型检查。For example, the static type checker did not report an error when assigning a to s even though s was declared to be of type str and receives an int value at runtime!

此外,没有返回类型或参数类型的所有函数将默认使用Any

def legacy_parser(text):
    ...
    return data

# A static type checker will treat the above
# as having the same signature as:
def legacy_parser(text: Any) -> Any:
    ...
    return data

当您需要混合动态和静态类型代码时,此行为允许Any用作escape hatch

对比Anyobject行为的行为。Any类似,每个类型都是object的子类型。但是,与Any不同,反之亦然:object而不是每个其他类型的子类型。

这意味着当值的类型是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.Any

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

  • 任何对象都是Any的实例。
  • 任何类都是Any的子类。
  • 作为特殊情况,Anyobject是彼此的子类。
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 (字节, 字节) - > 字节还要注意,如果参数是str的某个子类的实例,则返回类型仍然是平常的str

在运行时,运行时,isinstance(x, T)将引发TypeError一般来说,isinstance()issubclass()不应与类型一起使用。

类型变量可以通过传递covariant=Truecontravariant=True标记为协变或逆变。有关详细信息,请参阅 PEP 484默认类型变量是不变的。或者,类型变量可以使用bound=<type>来指定上界。这意味着对类型变量替换(显式或隐式)的实际类型必须是边界类型的子类型,请参见 PEP 484

class typing.Union

共用体类型; 联合[X, Y]表示X或Y.

要定义共用体,请使用e。Union [int, str]细节:

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

  • 工会联合会是扁平的,

    Union[Union[int, str], float] == Union[int, str, float]
    
  • 单一参数的联合消失,e。

    Union[int] == int  # The constructor actually returns int
    
  • 跳过冗余参数,e。

    Union[int, str, int] == Union[int, str]
    
  • 当比较联合时,参数顺序被忽略,e。

    Union[int, str] == Union[str, int]
    
  • 如果Any存在,它是唯一的幸存者,

    Union[int, Any] == Any
    
  • 你不能子类化或实例化一个共用体。

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

  • 您可以使用Optional[X]作为联合的缩写[X, 无]

class typing.Optional

可选类型。

Optional[X]等效于Union [X, type(None)]

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

class typing.Tuple

元组类型; 元组[X, Y]是两个项目的元组的类型,第一个项目类型为X,第二个项目类型为Y.

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

要指定同构类型的变量元组,请使用字面值省略号,Tuple [int, ...]

class typing.Callable

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

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

没有用于指示可选或关键字参数的语法,这种函数类型很少用作回调类型。Callable [..., ReturnType]可用于输入hint一个可调用的任何数量的参数并返回ReturnType平常Callable等效于Callable [..., Any]

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.Iterable(Generic[T_co])

collections.abc.Iterable

class typing.Iterator(Iterable[T_co])

collections.abc.Iterator

class typing.SupportsInt

具有一个抽象方法的ABC __int__

class typing.SupportsFloat

具有一个抽象方法的ABC:__float__

class typing.SupportsAbs

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

class typing.SupportsRound

具有一个抽象方法的ABC,它具有在其返回类型中协变的一个抽象方法__round__

class typing.Reversible

具有一个抽象方法的ABC __reversed__返回Iterator[T_co]

class typing.Container(Generic[T_co])

collections.abc.Container

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

collections.abc.Set

class typing.MutableSet(AbstractSet[T])

collections.abc.MutableSet

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

collections.abc.Mapping

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

collections.abc.MutableMapping

class typing.Sequence(Sized, Iterable[T_co], Container[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.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.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.Generator(Iterator[T_co], Generic[T_co, T_contra, V_co])

生成器可以通过通用类型注释生成器[YieldType, SendType,SendType, ReturnType]例如:

def echo_round() -> Generator[int, float, str]:
    sent = yield 0
    while sent >= 0:
        sent = yield round(sent)
    return 'Done'

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

如果您的生成器只会产生值,请将SendTypeReturnType设置为None

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

或者,将生成器注释为返回类型Iterator[YieldType]

def infinite_stream(start: int) -> Iterator[int]:
    while True:
        yield start
        start += 1
class typing.io

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

This defines the generic type IO[AnyStr] and aliases TextIO and BinaryIO for respectively IO[str] and IO[bytes]. 这些表示由open()返回的I / O流的类型。

class typing.re

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

这定义了类型别名PatternMatch,它们对应于来自re.compile()re.match()These types (and the corresponding functions) are generic in AnyStr and can be made specific by writing Pattern[str], Pattern[bytes], Match[str], or Match[bytes].

typing.NamedTuple(typename, fields)

namedtuple的类型化版本。

用法:

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

这相当于:

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

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

typing.cast(typ, val)

将值转换为类型。

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

typing.get_type_hints(obj)

返回函数或方法对象的类型提示。

这通常与obj.__annotations__相同,但它处理编码为字符串字面值的正向引用,字面值,如果必要,添加Optional[t]设置为None。

@typing.no_type_check(arg)

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

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

这会改变函数的位置。

@typing.no_type_check_decorator(decorator)

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

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