PEP 484 —— 类型注解

PEP 484 —— Type Hints



PEP 3107 引入了函数注解的语法,但语义保留为未定义。目前第三方的静态类型分析应用工具已经足够多了,社区开发者采用标准用语和标准库中的基线工具将会获益良多。

PEP 3107 introduced syntax for function annotations, but the semantics were deliberately left undefined. There has now been enough 3rd party usage for static type analysis that the community would benefit from a standard vocabulary and baseline tools within the standard library.

本 PEP 引入了一个假定的模块以提供这些标准的定义和工具,还有一些对于注解不可用的情形的约定。

This PEP introduces a provisional module to provide these standard definitions and tools, along with some conventions for situations where annotations are not available.

请注意,本 PEP 仍然明确不会阻止注解的其他用途,也不需要(或禁止)对注解的进行任何特定处理,即使它们符合本规范也是如此。正如 PEP 333 对 Web 框架的约定,这只是为了能更好地相互合作。

Note that this PEP still explicitly does NOT prevent other uses of annotations, nor does it require (or forbid) any particular processing of annotations, even when they conform to this specification. It simply enables better coordination, as PEP 333 did for web frameworks.


For example, here is a simple function whose argument and return type are declared in the annotations:

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

虽然这些注解可以在运行时通过常规属性 __annotations__ 访问到,但运行时并不会进行类型检查。相反,本提案假定存在一个独立的离线类型检查器,用户可以自愿地使用它对源代码进行类型检查。在本质上,这样的类型检查器充当了一个非常强大的静态代码分析工具。(虽然一些用户可以在运行时使用类似的检查工具来实现契约式设计和 JIT 优化,但目前这些工具都还不够成熟。)

While these annotations are available at runtime through the usual __annotations__ attribute, no type checking happens at runtime. Instead, the proposal assumes the existence of a separate off-line type checker which users can run over their source code voluntarily. Essentially, such a type checker acts as a very powerful linter. (While it would of course be possible for individual users to employ a similar checker at run time for Design By Contract enforcement or JIT optimization, those tools are not yet as mature.)

这份提案受到 mypy 的强烈启发。例如,“整数序列”的类型使用 Sequence[int] 表示。使用方括号意味着不需要在语言中添加新的语法。示例中使用了从纯 Python 模块 typing 中导入的定制类型 Sequence。通过在其元类中实现 __getItem __()Sequence[int] 标记得以在检查工具运行时生效(但这个标记主要是对离线类型检查器有意义)。

The proposal is strongly inspired by mypy [mypy]. For example, the type "sequence of integers" can be written as Sequence[int]. The square brackets mean that no new syntax needs to be added to the language. The example here uses a custom type Sequence, imported from a pure-Python module typing. The Sequence[int] notation works at runtime by implementing __getitem__() in the metaclass (but its significance is primarily to an offline type checker).

类型系统支持联合类型、范型类型、以及名为 Any 的可与任何类型兼容的特殊类型(可以赋值给任何类型,也可以被任何类型赋值)。Any 的特性取自渐进定型(gradual typing)的理念。PEP 483 中解释了渐进定型和全类型系统。

The type system supports unions, generic types, and a special type named Any which is consistent with (i.e. assignable to and from) all types. This latter feature is taken from the idea of gradual typing. Gradual typing and the full type system are explained in PEP 483.

PEP 482 中描述了我们借鉴的一些方案或对比的方案。

Other approaches from which we have borrowed or to which ours can be compared and contrasted are described in PEP 482.


Rationale and Goals

PEP 3107 添加了对任意函数定义部分的注解支持。尽管没有给注解的实际含义,但已经有了一个隐含的目标,即将注解用于类型提示 gvr-artima,这在 PEP 3107 中是被列出的第一个可能用例。

PEP 3107 added support for arbitrary annotations on parts of a function definition. Although no meaning was assigned to annotations then, there has always been an implicit goal to use them for type hinting [gvr-artima], which is listed as the first possible use case in said PEP.

本 PEP 旨在为类型注解提供标准的语法,使 Python 代码可以更轻松地进行静态分析和重构、潜在的运行时类型检查,以及(可能在某些上下文中)使用类型信息生成代码。

This PEP aims to provide a standard syntax for type annotations, opening up Python code to easier static analysis and refactoring, potential runtime type checking, and (perhaps, in some contexts) code generation utilizing type information.

这些目标中静态分析是最重要的。这包括支持如 mypy 之类的离线类型检查器,以及提供可由 IDE 使用的标准表示法,用于代码补全和重构。

Of these goals, static analysis is the most important. This includes support for off-line type checkers such as mypy, as well as providing a standard notation that can be used by IDEs for code completion and refactoring.



虽然本提案所提出的 typing 模块将包含一些用于运行时类型检查的构建块——特别是 get_type_hints() 函数——必须开发第三方包才能实现特定的运行时类型检查功能,例如使用装饰器或元类。至于使用类型提示进行性能优化,就留下作为读者的练习吧。

While the proposed typing module will contain some building blocks for runtime type checking -- in particular the get_type_hints() function -- third party packages would have to be developed to implement specific runtime type checking functionality, for example using decorators or metaclasses. Using type hints for performance optimizations is left as an exercise for the reader.

还应强调,Python 仍然是一门动态类型的语言,作者也不强求必须使用类型注解。

It should also be emphasized that Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.


The meaning of annotations

对任何类型检查器而言,任何没有注解的函数都应该被视为可能拥有最为通用的类型,或是直接忽略。使用 @no_type_check 装饰的函数视为没有任何注解。

Any function without annotations should be treated as having the most general type possible, or ignored, by any type checker. Functions with the @no_type_check decorator should be treated as having no annotations.

建议但是不必须要求被检查的函数的所有参数和返回值都有注解。对于一个被检查的函数,其参数和返回值的默认注释都为 Any。一个问题:实例方法和类方法的第一个参数,如果其没有注解,就假定实例方法第一个参数为所属类的类型,类方法的第一个参数。例如,在 class A 中,实例方法第一个参数的类型隐含为 A,在类方法中,第一个参数的精确类型无法用类型注解描述。

It is recommended but not required that checked functions have annotations for all arguments and the return type. For a checked function, the default annotation for arguments and for the return type is Any. An exception is the first argument of instance and class methods. If it is not annotated, then it is assumed to have the type of the containing class for instance methods, and a type object type corresponding to the containing class object for class methods. For example, in class A the first argument of an instance method has the implicit type A. In a class method, the precise type of the first argument cannot be represented using the available type notation.

(注意:__init__ 函数的返回值应当使用 -> None 注解,理由十分微妙,如果假定 __init__-> None 进行返回值注解,那是否意味着无参的、无注解的 __init__ 函数仍需要被类型检查?与其模棱两可或是引入异常,不如我们规定 __init__ 应当有一个返回值注解,默认行为与其他函数保持一致。)

(Note that the return type of __init__ ought to be annotated with -> None. The reason for this is subtle. If __init__ assumed a return annotation of -> None, would that mean that an argument-less, un-annotated __init__ method should still be type-checked? Rather than leaving this ambiguous or introducing an exception to the exception, we simply say that __init__ ought to have a return annotation; the default behavior is thus the same as for other methods.)


A type checker is expected to check the body of a checked function for consistency with the given annotations. The annotations may also be used to check correctness of calls appearing in other checked functions.


Type checkers are expected to attempt to infer as much information as necessary. The minimum requirement is to handle the builtin decorators @property, @staticmethod and @classmethod.


Type Definition Syntax


The syntax leverages PEP 3107-style annotations with a number of extensions described in sections below. In its basic form, type hinting is used by filling function annotation slots with classes:

这里的语法充分利用了 PEP 3107 风格的注解,并加入了以下章节介绍的一些扩展。基本格式就是把类名填到函数注解的位置:

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

This states that the expected type of the name argument is str. Analogically, the expected return type is str.

以上表示参数 name 的预期类型为 str。类似地,函数的预期返回类型为 str

Expressions whose type is a subtype of a specific argument type are also accepted for that argument.


Acceptable type hints


Type hints may be built-in classes (including those defined in standard library or third-party extension modules), abstract base classes, types available in the types module, and user-defined classes (including those defined in the standard library or third-party modules).

类型提示可以是内置类(包括标准库或第三方扩展模块中定义的类),抽象基类,类型模块中可用的类型,以及用户定义的类(包括标准库中定义的类或第三类) 派对模块)。

While annotations are normally the best format for type hints, there are times when it is more appropriate to represent them by a special comment, or in a separately distributed stub file. (See below for examples.)

虽然注释通常是类型提示的最佳格式,但有时会在特殊的评论中或在单独分布的存根文件中更合适地表示它们。 (见下面的例子。)

Annotations must be valid expressions that evaluate without raising exceptions at the time the function is defined (but see below for forward references).


Annotations should be kept simple or static analysis tools may not be able to interpret the values. For example, dynamically computed types are unlikely to be understood. (This is an intentionally somewhat vague requirement, specific inclusions and exclusions may be added to future versions of this PEP as warranted by the discussion.)

注释应该保持简单或静态分析工具可能无法解释值。 例如,动态计算的类型不太可能被理解。 (这是一个故意有些模糊要求,可以通过讨论所需的本文的未来版本添加特定的夹杂物和排除。)

In addition to the above, the following special constructs defined below may be used: None, Any, Union, Tuple, Callable, all ABCs and stand-ins for concrete classes exported from typing (e.g. Sequence and Dict), type variables, and type aliases.

除了上面的外,可以使用以下定义的以下特殊构造:无,从键入(例如序列和区域),型变量和类型的混凝土类中,任何,Union,元组,可调用,所有abcs和its-ince 别名。

All newly introduced names used to support features described in following sections (such as Any and Union) are available in the typing module.


使用 None

Using None

None 用于类型提示中时,表达式 None 视作与 type(None) 等价。

When used in a type hint, the expression None is considered equivalent to type(None).


Type aliases


Type aliases are defined by simple variable assignments:

Url = str

def retry(url: Url, retry_count: int) -> None: ...

Note that we recommend capitalizing alias names, since they represent user-defined types, which (like user-defined classes) are typically spelled that way.

Type aliases may be as complex as type hints in annotations -- anything that is acceptable as a type hint is acceptable in a type alias:

from typing import TypeVar, Iterable, Tuple

T = TypeVar('T', int, float, complex)
Vector = Iterable[Tuple[T, T]]

def inproduct(v: Vector[T]) -> T:
    return sum(x*y for x, y in v)
def dilate(v: Vector[T], scale: T) -> Vector[T]:
    return ((x * scale, y * scale) for x, y in v)
vec = []  # type: Vector[float]

This is equivalent to:


from typing import TypeVar, Iterable, Tuple

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

def inproduct(v: Iterable[Tuple[T, T]]) -> T:
    return sum(x*y for x, y in v)
def dilate(v: Iterable[Tuple[T, T]], scale: T) -> Iterable[Tuple[T, T]]:
    return ((x * scale, y * scale) for x, y in v)
vec = []  # type: Iterable[Tuple[float, float]]



如果框架期待具体特征的回调函数(callback functions),可以使用Callable[[Arg1Type, Arg2Type], ReturnType]来进行类型提示。例如:

Frameworks expecting callback functions of specific signatures might be type hinted using Callable[[Arg1Type, Arg2Type], ReturnType]. Examples:

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


It is possible to declare the return type of a callable without specifying the call signature by substituting a literal ellipsis (three dots) for the list of arguments:

def partial(func: Callable[..., str], *args) -> Callable[..., str]:
    # Body

Note that there are no square brackets around the ellipsis. The arguments of the callback are completely unconstrained in this case (and keyword arguments are acceptable).

请注意,省略号周围没有方形括号。 回调的参数在这种情况下完全无规定(并且关键字参数是可接受的)。

Since using callbacks with keyword arguments is not perceived as a common use case, there is currently no support for specifying keyword arguments with Callable. Similarly, there is no support for specifying callback signatures with a variable number of arguments of a specific type.

由于使用带关键字参数的回调不被视为常用用例,因此目前不支持使用可调用的可调用关键字参数。 类似地,没有支持使用特定类型的可变数量的参数指定回调签名。

Because typing.Callable does double-duty as a replacement for collections.abc.Callable, isinstance(x, typing.Callable) is implemented by deferring to isinstance(x, collections.abc.Callable). However, isinstance(x, typing.Callable[...]) is not supported.

因为 type.Callable 带有双重职能,用于替代 collections.abc.Callable,所以 isinstance(x, typing.Callable) 的实现与 isinstance(x, collections.abc.Callable) 兼容。但是,isinstance(x, typing.Callable[...]) 是不受支持的。



因为容器中保存的对象的类型信息不能以通用的方式静态推断,抽象基类被扩展为支持预定(subscription ),以标明容器内元素的期望类型。例如:

Since type information about objects kept in containers cannot be statically inferred in a generic way, abstract base classes have been extended to support subscription to denote expected types for container elements. Example:

from typing import Mapping, Set

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

泛型可以通过使用typing模块提供的新的工厂函数TypeVar 的来进行参数化。 例如:

Generics can be parameterized by using a new factory available in typing called TypeVar. Example:

from typing import Sequence, TypeVar

T = TypeVar('T')      # 定义类型变量T

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


In this case the contract is that the returned value is consistent with the elements held by the collection.


A TypeVar() expression must always directly be assigned to a variable (it should not be used as part of a larger expression). The argument to TypeVar() must be a string equal to the variable name to which it is assigned. Type variables must not be redefined.

TypeVar 支持把参数可能的类型限为一组固定值(注意:固定值类型不能用类型变量实现参数化)。例如,我们可以定义一个仅包含strbytes的类型变量。默认情况下,TypeVar定义的类型变量会包含所有可能的类型。以下是一个约束类型变量范围的示例:

TypeVar supports constraining parametric types to a fixed set of possible types (note: those types cannot be parameterized by type variables). For example, we can define a type variable that ranges over just str and bytes. By default, a type variable ranges over all possible types. Example of constraining a type variable:

from typing import TypeVar, Text

AnyStr = TypeVar('AnyStr', Text, bytes)

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


The function concat can be called with either two str arguments or two bytes arguments, but not with a mix of str and bytes arguments.


There should be at least two constraints, if any; specifying a single constraint is disallowed.


Subtypes of types constrained by a type variable should be treated as their respective explicitly listed base types in the context of the type variable. Consider this example:

class MyStr(str): ...

x = concat(MyStr('apple'), MyStr('pie'))

上述调用是合法的,只是类型变量 AnyStr 将被设为 str 而非 MyStr。实际上,赋给 x 的返回值,其推断类型也会是 str

The call is valid but the type variable AnyStr will be set to str and not MyStr. In effect, the inferred type of the return value assigned to x will also be str.

此外,由于Any是任何类型变量的有效值。 考虑下面这个例子:

Additionally, Any is a valid value for every type variable. Consider the following:

def count_truthy(elements: List[Any]) -> int:
    return sum(1 for elem in elements if elem)

这相当于删除了泛型注解直接写上:elements: List

This is equivalent to omitting the generic notation and just saying elements: List.


User-defined generic types

通过继承 Generic 基类,可将用户自定义类定义为泛型类。例如:

You can include a Generic base class to define a user-defined class as generic. Example:

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[T] as a base class defines that the class LoggedVar takes a single type parameter T. This also makes T valid as a type within the class body.

Generic 基类会用到定义了 __getitem__ 的元类,以便 LoggedVar[t] 能作为类型来使用:

The Generic base class uses a metaclass that defines __getitem__ so that LoggedVar[t] is valid as a type:

from typing import Iterable

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


A generic type can have any number of type variables, and type variables may be constrained. This is valid:

from typing import TypeVar, Generic

T = TypeVar('T')
S = TypeVar('S')

class Pair(Generic[T, S]):

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

Each type variable argument to Generic must be distinct. This is thus invalid:

from typing import TypeVar, Generic

T = TypeVar('T')

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

在比较简单的场合,没有必要用到 Generic[T],这时可以继承其他的泛型类并指定类型变量参数:

The Generic[T] base class is redundant in simple cases where you subclass some other generic class and specify type variables for its parameters:

from typing import TypeVar, Iterator

T = TypeVar('T')

class MyIter(Iterator[T]):


That class definition is equivalent to:

class MyIter(Iterator[T], Generic[T]):

可以对 Generic 使用多重继承:

You can use multiple inheritance with Generic:

from typing import TypeVar, Generic, Sized, Iterable, Container, Tuple

T = TypeVar('T')

class LinkedList(Sized, Generic[T]):

K = TypeVar('K')
V = TypeVar('V')

class MyMapping(Iterable[Tuple[K, V]],
                Container[Tuple[K, V]],
                Generic[K, V]):

如未指定类型参数,则泛型类的子类会假定参数的类型均为 Any。在以下示例中,MyIterable 就不是泛型类,而是隐式继承自 Iterable[Any]

Subclassing a generic class without specifying type parameters assumes Any for each position. In the following example, MyIterable is not generic but implicitly inherits from Iterable[Any]:

from typing import Iterable

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


Generic metaclasses are not supported.


Scoping rules for type variables


Type variables follow normal name resolution rules. However, there are some special cases in the static typechecking context:

  • 即使是在相同的代码块中,泛型函数中使用的类型变量也可以被推断为不同的类型,例如:
  • A type variable used in a generic function could be inferred to represent different types in the same code block. Example:
from typing import TypeVar, Generic

T = TypeVar('T')

def fun_1(x: T) -> T: ...  # T here
def fun_2(x: T) -> T: ...  # 此处的T可以和上面的不同

fun_1(1)                   # This is OK, T is inferred to be int
fun_2('a')                 # This is also OK, now T is str
  • A type variable used in a method of a generic class that coincides with one of the variables that parameterize this class is always bound to that variable. Example:
  • 在泛型类中,函数中的类型变量将始终和参数化这个类的变量保持一致,例如:
from typing import TypeVar, Generic

T = TypeVar('T')

class MyClass(Generic[T]):
    def meth_1(self, x: T) -> T: ...  # T here
    def meth_2(self, x: T) -> T: ...  # 此处T和上面的保持一致

a = MyClass()  # type: MyClass[int]
a.meth_1(1)    # OK
a.meth_2('a')  # This is an error!
  • A type variable used in a method that does not match any of the variables that parameterize the class makes this method a generic function in that variable:
  • 在泛型类中,若函数中的某类型变量和参数化这个类的任何变量都不匹配,将使该函数成为一个使用该类型的泛型函数。
T = TypeVar('T')
S = TypeVar('S')
class Foo(Generic[T]):
    def method(self, x: T, y: S) -> S:

x = Foo()               # type: Foo[int]
y = x.method(0, "abc")  # 推断y的类型为str
  • Unbound type variables should not appear in the bodies of generic functions, or in the class bodies apart from method definitions:
  • 未绑定的类型变量不应出现在泛型函数内,类的内部中的非函数定义区域也是如此:
T = TypeVar('T')
S = TypeVar('S')

def a_fun(x: T) -> None:
    # this is OK
    y = []  # type: List[T]
    # but below is an error!
    y = []  # type: List[S]

class Bar(Generic[T]):
    # this is also an error
    an_attr = []  # type: List[S]

    def do_something(x: S) -> S:  # this is OK though
  • A generic class definition that appears inside a generic function should not use type variables that parameterize the generic function:
  • 在一个泛型函数内,泛型类的定义中不应该使用参数化这个函数的类型变量:
from typing import List

def a_fun(x: T) -> None:

    # This is OK
    a_list = []  # type: List[T]

    # This is however illegal
    class MyGeneric(Generic[T]):
  • A generic class nested in another generic class cannot use same type variables. The scope of the type variables of the outer class doesn't cover the inner one:
  • 嵌套在其他泛型类中的泛型类不能使用相同的类型变量,外部类中类型变量的作用域并不能覆盖到内部类中:
T = TypeVar('T')
S = TypeVar('S')

class Outer(Generic[T]):
    class Bad(Iterable[T]):       # Error
    class AlsoBad:
        x = None  # type: List[T] # Also an error

    class Inner(Iterable[S]):     # OK
    attr = None  # type: Inner[T] # Also OK

Instantiating generic classes and type erasure

User-defined generic classes can be instantiated. Suppose we write a Node class inheriting from Generic[T]:

from typing import TypeVar, Generic

T = TypeVar('T')

class Node(Generic[T]):

To create Node instances you call Node() just as for a regular class. At runtime the type (class) of the instance will be Node. But what type does it have to the type checker? The answer depends on how much information is available in the call. If the constructor (__init__ or __new__) uses T in its signature, and a corresponding argument value is passed, the type of the corresponding argument(s) is substituted. Otherwise, Any is assumed. Example:

from typing import TypeVar, Generic

T = TypeVar('T')

class Node(Generic[T]):
    x = None  # type: T # Instance attribute (see below)
    def __init__(self, label: T = None) -> None:

x = Node('')  # Inferred type is Node[str]
y = Node(0)   # Inferred type is Node[int]
z = Node()    # Inferred type is Node[Any]

In case the inferred type uses [Any] but the intended type is more specific, you can use a type comment (see below) to force the type of the variable, e.g.:

# (continued from previous example)
a = Node()  # type: Node[int]
b = Node()  # type: Node[str]

Alternatively, you can instantiate a specific concrete type, e.g.:

# (continued from previous example)
p = Node[int]()
q = Node[str]()
r = Node[int]('')  # Error
s = Node[str](0)   # Error

Note that the runtime type (class) of p and q is still just Node -- Node[int] and Node[str] are distinguishable class objects, but the runtime class of the objects created by instantiating them doesn't record the distinction. This behavior is called "type erasure"; it is common practice in languages with generics (e.g. Java, TypeScript).

Using generic classes (parameterized or not) to access attributes will result in type check failure. Outside the class definition body, a class attribute cannot be assigned, and can only be looked up by accessing it through a class instance that does not have an instance attribute with the same name:

# (continued from previous example)
Node[int].x = 1  # Error
Node[int].x      # Error
Node.x = 1       # Error
Node.x           # Error
type(p).x        # Error
p.x              # Ok (evaluates to None)
Node[int]().x    # Ok (evaluates to None)
p.x = 1          # Ok, but assigning to instance attribute

Generic versions of abstract collections like Mapping or Sequence and generic versions of built-in classes -- List, Dict, Set, and FrozenSet -- cannot be instantiated. However, concrete user-defined subclasses thereof and generic versions of concrete collections can be instantiated:

data = DefaultDict[int, bytes]()

Note that one should not confuse static types and runtime classes. The type is still erased in this case and the above expression is just a shorthand for:

data = collections.defaultdict()  # type: DefaultDict[int, bytes]

It is not recommended to use the subscripted class (e.g. Node[int]) directly in an expression -- using a type alias (e.g. IntNode = Node[int]) instead is preferred. (First, creating the subscripted class, e.g. Node[int], has a runtime cost. Second, using a type alias is more readable.)

Arbitrary generic types as base classes

Generic[T] is only valid as a base class -- it's not a proper type. However, user-defined generic types such as LinkedList[T] from the above example and built-in generic types and ABCs such as List[T] and Iterable[T] are valid both as types and as base classes. For example, we can define a subclass of Dict that specializes type arguments:

from typing import Dict, List, Optional

class Node:

class SymbolTable(Dict[str, List[Node]]):
    def push(self, name: str, node: Node) -> None:
        self.setdefault(name, []).append(node)

    def pop(self, name: str) -> Node:
        return self[name].pop()

    def lookup(self, name: str) -> Optional[Node]:
        nodes = self.get(name)
        if nodes:
            return nodes[-1]
        return None

SymbolTable is a subclass of dict and a subtype of Dict[str, List[Node]].

If a generic base class has a type variable as a type argument, this makes the defined class generic. For example, we can define a generic LinkedList class that is iterable and a container:

from typing import TypeVar, Iterable, Container

T = TypeVar('T')

class LinkedList(Iterable[T], Container[T]):

Now LinkedList[int] is a valid type. Note that we can use T multiple times in the base class list, as long as we don't use the same type variable T multiple times within Generic[...].

Also consider the following example:

from typing import TypeVar, Mapping

T = TypeVar('T')

class MyDict(Mapping[str, T]):

In this case MyDict has a single parameter, T.

Abstract generic types

The metaclass used by Generic is a subclass of abc.ABCMeta. A generic class can be an ABC by including abstract methods or properties, and generic classes can also have ABCs as base classes without a metaclass conflict.


Type variables with an upper bound


A type variable may specify an upper bound using bound= (note: itself cannot be parameterized by type variables). This means that an actual type substituted (explicitly or implicitly) for the type variable must be a subtype of the boundary type. Example:

from typing import TypeVar, Sized

ST = TypeVar('ST', bound=Sized)

def longer(x: ST, y: ST) -> ST:
    if len(x) > len(y):
        return x
        return y

longer([1], [1, 2])  # ok, return type List[int]
longer({1}, {1, 2})  # ok, return type Set[int]
longer([1], {1, 2})  # ok, return type Collection[int]

An upper bound cannot be combined with type constraints (as in used AnyStr, see the example earlier); type constraints cause the inferred type to be exactly one of the constraint types, while an upper bound just requires that the actual type is a subtype of the boundary type.

Covariance and contravariance

Consider a class Employee with a subclass Manager. Now suppose we have a function with an argument annotated with List[Employee]. Should we be allowed to call this function with a variable of type List[Manager] as its argument? Many people would answer "yes, of course" without even considering the consequences. But unless we know more about the function, a type checker should reject such a call: the function might append an Employee instance to the list, which would violate the variable's type in the caller.

It turns out such an argument acts contravariantly, whereas the intuitive answer (which is correct in case the function doesn't mutate its argument!) requires the argument to act covariantly. A longer introduction to these concepts can be found on Wikipedia [wiki-variance] and in PEP 483; here we just show how to control a type checker's behavior.

By default generic types are considered invariant in all type variables, which means that values for variables annotated with types like List[Employee] must exactly match the type annotation -- no subclasses or superclasses of the type parameter (in this example Employee) are allowed.

To facilitate the declaration of container types where covariant or contravariant type checking is acceptable, type variables accept keyword arguments covariant=True or contravariant=True. At most one of these may be passed. Generic types defined with such variables are considered covariant or contravariant in the corresponding variable. By convention, it is recommended to use names ending in _co for type variables defined with covariant=True and names ending in _contra for that defined with contravariant=True.

A typical example involves defining an immutable (or read-only) container class:

from typing import TypeVar, Generic, Iterable, Iterator

T_co = TypeVar('T_co', covariant=True)

class ImmutableList(Generic[T_co]):
    def __init__(self, items: Iterable[T_co]) -> None: ...
    def __iter__(self) -> Iterator[T_co]: ...

class Employee: ...

class Manager(Employee): ...

def dump_employees(emps: ImmutableList[Employee]) -> None:
    for emp in emps:

mgrs = ImmutableList([Manager()])  # type: ImmutableList[Manager]
dump_employees(mgrs)  # OK

The read-only collection classes in typing are all declared covariant in their type variable (e.g. Mapping and Sequence). The mutable collection classes (e.g. MutableMapping and MutableSequence) are declared invariant. The one example of a contravariant type is the Generator type, which is contravariant in the send() argument type (see below).

Note: Covariance or contravariance is not a property of a type variable, but a property of a generic class defined using this variable. Variance is only applicable to generic types; generic functions do not have this property. The latter should be defined using only type variables without covariant or contravariant keyword arguments. For example, the following example is fine:

from typing import TypeVar

class Employee: ...

class Manager(Employee): ...

E = TypeVar('E', bound=Employee)

def dump_employee(e: E) -> None: ...

dump_employee(Manager())  # OK

while the following is prohibited:

B_co = TypeVar('B_co', covariant=True)

def bad_func(x: B_co) -> B_co:  # Flagged as error by a type checker


The numeric tower

PEP 3141定义了Python数字类型的层级关系,stdlib模块中的numbers实现了相应的抽象基类(Number, Complex, Real, RationalIntegral)。关于这些抽象基类还有一些争议,但是内置的数字类型如complexfloat还有int已经在普遍使用了(尤其是后两者)。

PEP 3141 defines Python's numeric tower, and the stdlib module numbers implements the corresponding ABCs (Number, Complex, Real, Rational and Integral). There are some issues with these ABCs, but the built-in concrete numeric classes complex, float and int are ubiquitous (especially the latter two :-).

与其让用户去写 import numbers 来导入然后使用如 Float 等数字类,本PEP提供了一种简洁、高效的途径:只要注解为 float 类型的参数,使用 int 类型也是合法的,同理,注解为 complex 类型的参数,使用 floatint 类型也是合法的。但缺陷是无法处理实现了上述抽象基类的类或是实现了 fractions.Fraction 的类,不过我们相信这种情况十分罕见。

Rather than requiring that users write import numbers and then use numbers.Float etc., this PEP proposes a straightforward shortcut that is almost as effective: when an argument is annotated as having type float, an argument of type int is acceptable; similar, for an argument annotated as having type complex, arguments of type float or int are acceptable. This does not handle classes implementing the corresponding ABCs or the fractions.Fraction class, but we believe those use cases are exceedingly rare.


Forward references


When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.


A situation where this occurs commonly is the definition of a container class, where the class being defined occurs in the signature of some of the methods. For example, the following code (the start of a simple binary tree implementation) does not work:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

To address this, we write:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

The string literal should contain a valid Python expression (i.e., compile(lit, '', 'eval') should be a valid code object) and it should evaluate without errors once the module has been fully loaded. The local and global namespace in which it is evaluated should be the same namespaces in which default arguments to the same function would be evaluated.

Moreover, the expression should be parseable as a valid type hint, i.e., it is constrained by the rules from the section Acceptable type hints above.

It is allowable to use string literals as part of a type hint, for example:

class Tree:
    def leaves(self) -> List['Tree']:

A common use for forward references is when e.g. Django models are needed in the signatures. Typically, each model is in a separate file, and has methods taking arguments whose type involves other models. Because of the way circular imports work in Python, it is often not possible to import all the needed models directly:

# File models/a.py
from models.b import B
class A(Model):
    def foo(self, b: B): ...

# File models/b.py
from models.a import A
class B(Model):
    def bar(self, a: A): ...

# File main.py
from models.a import A
from models.b import B

Assuming main is imported first, this will fail with an ImportError at the line from models.a import A in models/b.py, which is being imported from models/a.py before a has defined class A. The solution is to switch to module-only imports and reference the models by their module.class name:

# File models/a.py
from models import b
class A(Model):
    def foo(self, b: 'b.B'): ...

# File models/b.py
from models import a
class B(Model):
    def bar(self, a: 'a.A'): ...

# File main.py
from models.a import A
from models.b import B


Union types

因为一个参数可接受数量有限的几种预期类型是常见需求,所以系统新提供了一个特殊的工厂类,名为 Union。例如:

Since accepting a small, limited set of expected types for a single argument is common, there is a new special factory called Union. Example:

from typing import Union

def handle_employees(e: Union[Employee, Sequence[Employee]]) -> None:
    if isinstance(e, Employee):
        e = [e]

Union[T1,T2,...]构造的类型是类型T1T2等的超类型,因此由Union注释的参数是可接受的,这是其中一个类型的值[T1 ,T2,...]

一个常见的Union类型是可选类型。 默认情况下,None是任何类型的一个非法值,除非在函数定义中提供了默认值。 例如:

def handle_employee(e: Union[Employee, None]) -> None: ...

你可以使用Optional[T1]作为Union[T1, None]的简写,例如,上述代码相当于:

from typing import Optional

def handle_employee(e: Optional[Employee]) -> None: ...

通过 Union 支持单例类型

Support for singleton types in unions

单实例通常用于标记某些特殊条件,特别是 None 也是合法变量值的情况下。例如:

_empty = object()

def func(x=_empty):
    if x is _empty:  # 默认参数的情况
        return 0
    elif x is None:  # 入参为None的情况
        return 1
        return x * 2

为了在这种情况下允许精确设定类型,用户应结合使用 Union 类型和标准库提供的 enum.Enum 类,这样就能静态捕获类型错误了:

from typing import Union
from enum import Enum

class Empty(Enum):
    token = 0
_empty = Empty.token

def func(x: Union[int, None, Empty] = _empty) -> int:

    boom = x * 42  # 类型检查会提示错误

    if x is _empty:
        return 0
    elif x is None:
        return 1
    else:  # 类型检查器知道此处x的类型只能为int
        return x * 2

因为 Enum 的子类无法被继承(subclassed),所以上面的例子中所有分支里,变量x的类型都可以被静态推断。 多个单例对象的情况下也适用相同的方法:可以使用具有多个值的枚举:

class Reason(Enum):
    timeout = 1
    error = 2

def process(response: Union[str, Reason] = '') -> str:
    if response is Reason.timeout:
        return 'TIMEOUT'
    elif response is Reason.error:
        return 'ERROR'
        # response的类型只能是str, 因为其他所有的可能都被排除了
        return 'PROCESSED: ' + response


The Any type


当某个值的类型为 object 时,类型检查程序将拒绝几乎所有对其进行的操作,将其赋给类型更具体的变量(或将其用作返回值)将是一种类型错误。反之,当值的类型为Any时,类型检查程序将允许对其执行的所有操作,并且Any 类型的值可以赋给类型更具体(constrained)的变量(或用作返回值)。

类型检查器对不带类型注解的函数参数会假定其使用Any 注解。如果在没有指定类型参数的情况下使用泛型(generic type),则也假定其参数类型为 Any

from typing import Mapping

def use_map(m: Mapping) -> None:  # Same as Mapping[Any, Any]

此规则也适用于元组(Tuple),在类型注解的上下文中,它相当于Tuple[Any, ...]tuple也是如此。此外,在类型注解的上下文中Callable等效于Callable[..., Any]collections.abc.Callable也是如此。

from typing import Tuple, List, Callable

def check_args(args: Tuple) -> bool:

check_args(())           # OK
check_args((42, 'abc'))  # Also OK
check_args(3.14)         # 类型检查器会标记为错误

# 这个函数的入参为一个任意的可调用的(Callable)列表
def apply_callbacks(cbs: List[Callable]) -> None:


The NoReturn type


from typing import NoReturn

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


import sys
from typing import NoReturn

def f(x: int) -> NoReturn:  # Error, f(0) 隐含的返回None
    if x != 0:


# 上接第一个例子
def g(x: int) -> int:
    if x > 0:
        return x
    return 'whatever works'  # 一些检查器可能不会提示错误
                             # 因为它们会忽略不可达的代码块中的错误


from typing import List, NoReturn

# 以下都会提示错误
def bad1(x: NoReturn) -> int:
bad2 = None  # type: NoReturn
def bad3() -> List[NoReturn]:


The type of class objects

有些情况会对类对象有疑惑,特别是从给定类继承而来的类对象。当C是一个类,可以使用Type[C]来表示类对象C的类型。当C(在注解中使用的时候)指class C的实例时,Type[C]指的是C的子类。(这类似于对象和类型之间的区别。)


class User: ...  # Abstract base for User classes
class BasicUser(User): ...
class ProUser(User): ...
class TeamUser(User): ...


def new_user(user_class):
    user = user_class()
    # (此处我们可以把user对象写入数据库)
    return user


def new_user(user_class: type) -> User:

通过使用Type[]和一个向上绑定(upper bound)的类型变量,我们可以做的更好:

U = TypeVar('U', bound=User)
def new_user(user_class: Type[U]) -> U:


joe = new_user(BasicUser)  # 推断joe的类型为BasicUser

Type[C]相应的值必须是一个实际的类对象,且是C的子类型(subtype),非其特殊形式。换句话说,上述例子中如果这样调用new_user(Union[BasicUser, ProUser])是会被类型检查器拒绝接受的(如果运行的话也会失败,因为Union不能实例化)。


def new_non_team_user(user_class: Type[Union[BasicUser, ProUser]]):
    user = new_user(user_class)


new_non_team_user(ProUser)  # OK
new_non_team_user(TeamUser)  # Disallowed by type checker



Annotating instance and class methods


T = TypeVar('T', bound='Copyable')
class Copyable:
    def copy(self: T) -> T:
        # return a copy of self

class C(Copyable): ...
c = C()
c2 = c.copy()  # c2的类型为C

同样,类方法第一个参数可以使用Type[]进行注解 :

T = TypeVar('T', bound='C')
class C:
    def factory(cls: Type[T]) -> T:
        # make a new instance of cls

class D(C): ...
d = D.factory()  # d的类型为D

值得注意的是某些类型检查器可能对上述用法施加限制,比如要求类型变量具备合适的向上绑定(upper bound)类型,参见上例。


Version and platform checking


import sys

if sys.version_info[0] >= 3:
    # Python 3的特有定义语句
    # Python 2的特有定义语句

if sys.platform == 'win32':
    # Windows平台的特有定义语句
    # Posix平台的特有定义语句

不要期待检查器能够理解 "".join(reversed(sys.platform)) == "xunil" 这样的晦涩语句。


Runtime or type checking?

有时一些代码必须由类型检查器(或是其他静态分析工具)进行分析,但不能被运行。为了应对这种情况, typing 模块定义了一个常量—— TYPE_CHECKING ,它在类型检查期间(或是静态分析期间)为 True,运行时为 False ,例如:

import typing

if typing.TYPE_CHECKING:
    import expensive_mod

def a_func(arg: 'expensive_mod.SomeClass') -> None:
    a_var = arg  # type: expensive_mod.SomeClass

(注意类型注解必须使用引号进行包裹,使它成为“前向引用”,以便解释器运行时不去导入 expensive_mod 模块,另外使用 # type 进行类型注释时无需引号。)

这种方式也能够有效的处理循环导入(import cycles)。


Arbitrary argument lists and default argument values


def foo(*args: str, **kwds: int): ...


foo('a', 'b', 'c')
foo(x=1, y=2)
foo('', z=0)


Positional-only arguments


def quux(__x: int, __y__: int = 0) -> None: ...

quux(3, __y__=1)  # This call is fine.

quux(__x=3)  # This call is an error.

Annotating generator functions and coroutines


Compatibility with other uses of function annotations

存在一些函数注解的使用场景,是与类型提示不兼容的。这些场景可能导致静态类型检查器的混乱,但是由于类型检查注解并不在运行时生效(计算注解表达式、将注解存储在函数对象的 __annotations__ 属性中除外),所以这个并不会使程序发生错误——这仅可能造成类型检查器发出虚假的警告和报错。


  • # type: ignore 注释;
  • 在类或函数上使用 @no_type_check 装饰器;
  • 在自定义的类或函数装饰器中使用 @no_type_check_decorator 标记;


为了最大程度的和离线类型检查兼容,将依赖于注解的机制改为其他机制(如装饰器)可能是个更好的主意。不过这在 Python 3.5 中没什么关系。更多讨论请参见后续的“没有被接受的方案”。


Type comments


x = []                # type: List[Employee]
x, y, z = [], [], []  # type: List[int], List[int], List[str]
x, y, z = [], [], []  # type: (List[int], List[int], List[str])
a, b, *c = range(5)   # type: float, float, List[float]
x = [1, 2]            # type: List[int]

类型注释应放在包含变量定义的语句的最后一行中,还可以放在 withfor 语句的冒号后面,例如:

with frobnicate() as foo:  # type: int
    # Here foo is an int

for x, y in points:  # type: float, float
    # Here x and y are floats

在存根文件中,不给出初始值而仅申明变量,这可能会有帮助。通过PEP 526的变量注解语法即可实现:

from typing import IO

stream: IO[str]

上面的存根文件中的语法在所有版本的 Python 中均可接受,但是在 Python 3.5 之前的版本的非存根文件中,有一个特例:

from typing import IO

stream = None  # type: IO[str]

尽管 None 与给定的类型不符,类型检查器也不应该对此给出报错,也不应该将类型推断的结果修改为 Optional[...](尽管规则要求默认值为 None 的应该如此申明)。这里假定其他代码保证变量是给定的类型,并且以后的所有调用都假定该变量为该给定类型。

# type: ignore 注释应当放在提示错误的那一行

import http.client
errors = {
    'not_found': http.client.NOT_FOUND  # type: ignore

# type: ignore 注释在文件的头一行,或者在任何文档字符串之前,又或者在任何 import 和可执行代码之前,这表示忽略文件中的所有类型检查错误。空行或是其他注释(比如 shebang 注释或是 coding cookies)可以在其之前。

有些情况下,类型注释可能需要与语法检查工具或其他注释同处一行,此时类型注释应位于其他注释和 lint 标记之前:

# type: ignore # <comment or other marker>

如果大多时候类型提示能被证明有用,那么将来版本的 Python 可能会为 typing 变量提供语法。(更新:这些语法已在 Python 3.6 加入,详见PEP-526


x = dict(
)  # type: Dict[str, int]

cast 函数


有时类型检查器需要另一种提示:程序员可能知道某个表达式是比类型检查器所推断的类型更加精确的类型。 例如:

from typing import List, cast

def find_first_str(a: List[object]) -> str:
    index = next(i for i, x in enumerate(a) if isinstance(x, str))
    # We only get here if there's at least one string in a
    return cast(str, a[index])

某些类型检查器可能无法推断 a[index] 的类型是 str,或是只能推断为 objectAny。但我们知道(如果代码运行到此处)它必然是一个字符串。cast(t, x) 这个调用告诉类型检查器,我们确定 x 的类型是 t。 在运行时,cast 始终返回 x 的传入值——它不会检查类型,也不会强制转换该值。

cast 与类型注释(见上一节)不同。 使用类型注释时,类型检查器仍应验证推断类型是否与所述类型一致。 使用 cast 时,类型检查器应该无条件的相信程序员。 此外,cast 可以在表达式中使用,而类型注释仅适用于声明语句。

工具函数 NewType

NewType helper function


class UserId(int):

get_by_user_id(user_id: UserId):

然而这引入了额外的运行时开销,为了避免这种情况,typing.py提供了一个工具函数NewType用以创建简单的类型,而且几乎没有运行时开销。对于静态类型检查器而言Derived = NewType('Derived', Base)相当于如下定义:

class Derived(Base):
    def __init__(self, _x: Base) -> None:

当处于运行时,NewType('Derived', Base) 返回一个伪函数(dummy function),这个函数直接返回其传入值。当类型检查器期待 UserId 时,需要显式的将 int 转为 UserId,期待 int 时,UserId 会隐式的转换为 int,来看这个例子:

UserId = NewType('UserId', int)

def name_by_id(user_id: UserId) -> str:

UserId('user')          # Fails type check

name_by_id(42)          # Fails type check
name_by_id(UserId(42))  # OK

num = UserId(5) + 1     # type: int

NewType 只接受两个参数:一个新的唯一的类型名称和一个基类。后者应当为一个恰当的类(不是像 Union 这样的类型构造类,但可以是调用 NewType 生成的其他唯一类型)。NewType 返回的函数仅接受一个参数;这相当于一个只接受一个基类实例的构造器(参见上文)。例如:

class PacketId:
    def __init__(self, major: int, minor: int) -> None:
        self._major = major
        self._minor = minor

TcpPacketId = NewType('TcpPacketId', PacketId)

packet = PacketId(100, 100)
tcp_packet = TcpPacketId(packet)  # OK

tcp_packet = TcpPacketId(127, 0)  # Fails in type checker and at runtime

NewType('Derived', Base) 进行子类化,或是调用 isinstance 以及 issubclass 都会失败,这是因为函数对象不支持这些操作。


Stub Files


  • 扩展模块

  • 一些作者没有添加类型提示的第三方模块

  • 没有写类型提示的标准库模块
  • 一些必须兼容Python 2和 Python 3的模块
  • 出于其他考虑使用注解的模块

存根文件和常规的Python模块拥有相同的语法,typing 模块中有一项特性在存根文件中会有所不同: @overload 装饰器,下面会详细描述。

类型检查器应当只检查存根文件中的函数声明(function signatures),建议存根文件中的函数体只是简单的写上省略号 ...


尽管存根文件也是语法正确的Python模块,但它们使用 .pyi 后缀,这样存根文件可以和对应的真实模块文件放在同一目录。这样也加强了一个概念:存根文件不会在运行时有任何行为。


  • 除了在存根文件中使用 import ... as ... 或等效的 from ... import ... as ... 进行导入模块和变量,其他情况下导入到存根文件中的模块和变量都视为不能导出。(补充:说的更明白一些就是只有使用To clarify, the intention here is that only names imported using the form X as X will be exported, i.e. the name before and after as must be the same.)
  • 然而,上一条有一个例外,所有使用 from ... import * 导入存根文件的对象,都视为能被导出。这使得更容易从给定模块重新导出所有对象,因为这个模块会由于Python版本产生不同。

Function/method overloading

Storing and distributing stub files

The Typeshed Repo





The typing Module

Suggested syntax for Python 2.7 and straddling code


Rejected Alternatives




Which brackets for generic type parameters?

大多数人都熟悉使用 C++,Java,C# 和 Swift 的语言中的角度括号(例如 List<int>),以表达通用类型的参数化。 这些问题是他们真的很难解析,特别是对于像python这样的简单的解析器。 在大多数语言中,含糊不仅可以通过特定的句法位置中的角度括号来处理歧义,其中不允许常规表达式。 (并且还通过使用非常强大的解析技术,可以在代码的任意部分上返回备份。)

但是在 Python 中,我们希望键入表达式(句法)与其他表达式相同,以便我们可以使用e .. 可变分配来创建类型别名。 考虑此简单类型表达式:


从Python Parser的角度来看,表达式以相同的四个令牌(姓名,少,名称,更大)开始,作为链接的比较:

a < b > c  # I.e., (a < b) and (b > c)


a < b > [ c ]


(a<b>)[c]      # I.e., (a<b>).__getitem__(c)
a < b > ([c])  # I.e., (a < b) and (b > [c])

肯定有可能提出消除此类案件的规则,但对于大多数用户来说,规则将觉得任意和复杂。 它还要求我们大大改变CPython解析器(以及Python的每个其他解析器)。 应该指出的是,Python的当前解析器故意“愚蠢” - 一个简单的语法对用户更容易推理。

出于所有这些原因,方括号(例如,列表[int])是(并且长期以来)的通用类型参数的首选语法。 可以通过在Metaclass上定义__getItem __()方法来实现它们,并且根本不需要新的语法。 此选项在所有最近版本的Python中工作(Python 2.2起)。 Python在这个句法选择中并不孤单 - Scala中的通用类也使用方括号。

What about existing uses of annotations?

The problem of forward declarations

The double colon

Other forms of new syntax

Other backwards compatible conventions

PEP 开发过程

PEP Development Process

本 PEP 的最新文稿位于 GitHub 上。另有一个议题跟踪包含了很多技术讨论内容。

GitHub 上的文稿会定期进行小幅更新。通常正式的 PEPS 库只在新文稿发布到 python-dev 时才会更新。



没有 Jim Baker、Jeremy Siek、Michael Matson Vitousek、Andrey Vlasovskikh、Radomir Dopieralski、Peter Ludemann 和BDFL-Delegate、Mark Shannon 的宝贵投入、鼓励和建议,本文就无法完成。

本文受到 PEP 482 中提及的现有语言、库和框架的影响。非常感谢其创建者(按字母序排列):Stefan Behnel、William Edwards、Greg Ewing、Larry Hastings、Anders Hejlsberg、Alok Menghrajani、Travis E. Oliphant、Joe Pamer、Raoul-Gabriel Urma、and Julien Verlaguet。



[mypy] http://mypy-lang.org
[gvr-artima] http://www.artima.com/weblogs/viewpost.jsp?thread=85551
[wiki-variance] http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29
[typeshed] https://github.com/python/typeshed/
[pyflakes] https://github.com/pyflakes/pyflakes/
[pylint] http://www.pylint.org
[roberge] http://aroberge.blogspot.com/2015/01/type-hinting-in-python-focus-on.html
[github] https://github.com/python/typing
[issues] https://github.com/python/typing/issues
[peps] https://hg.python.org/peps/file/tip/pep-0484.txt
[importdocs] https://docs.python.org/3/reference/import.html#submodules




源码: https://github.com/python/peps/blob/master/pep-0484.txt