在自己维护了一个几万行的 python
项目后,才发现类型标注可以拯救你维护时候的抓狂。
python
作为一个动态语言,只有在运行的时候才知道该变量到底是什么类型,虽然,python
写起来非常快,但是,后期维护起来,不知道变量怎么取值是真的痛苦。
从 python 3.5
版本开始将 Typing
作为标准库引入。
参考资料
Type aliases
类型别名是通过将类型分配给别名来定义的。
1 | from typing import List |
NewType
使用 NewType
辅助类来创建不同的类型。静态类型检查器会将新类型视为原始类型的子类,这有助于捕获逻辑错误。
1 | from typing import NewType |
Any
是一种特殊的类型。静态类型检查器会将每种类型都视为与 Any
兼容,同样,Any
也与所有类型兼容。可以对Any
类型的值执行任何操作或方法调用,并将其分配给任何变量。将Any
类型的值分配给更精确的类型(more precise type)
时,不会执行类型检查。所有没有返回类型或参数类型的函数都将隐式地默认使用Any
。
Any
与object
的区别:与Any
类似,每个类型都是object
的子类型。然而,与Any
不同,反过来就不成立了:object
不是其它类型的子类型。
使用object
,说明值能以类型安全的方式转为任何类型;使用Any
,说明值是动态类型。
1 | a: Any = None |
NoReturn
特殊类型,标记一个函数没有返回值
1 | def stop() -> NoReturn: |
Union
联合类型,Union[X, Y]
等价于X|Y
,意味着X
或Y
。使用形如Union[int, str]
的形式来定义一个联合体
- 参数必须是某种类型,且至少有一个
- 联合类型的联合类型会被展开(
flattened
) - 仅有一个参数的联合类型就是该参数自身
- 冗余的参数会被跳过
(skipped)
- 在比较联合类型的时候,参数顺序会被忽略
(ignored)
- 不能继承或者实例化一个联合类型
- 不支持
Union[X][Y]
这种写法。
1 |
|
Optional
可选类型,Optional[X]
等价于X|None
,或Union[X, None]
。可选类型与含默认值的可选参数不同,含默认值的可选参数不需要在类型注解(type annotation)
上添加Optional
限定符,因为它仅是可选的。显式应用None
值时,不管该参数是否可选,Optional
都适用。
1 | # 可选类型与含默认值的可选参数不同,含默认值的可选参数不需要在类型注解(type annotation)上添加Optional限定符,因为它仅是可选的 |
Callable
可调用类型,下标语法(subscription syntax)
必须始终与两个值一起使用:参数列表和返回类型。参数列表必须是类型列表或省略号;返回类型必须是单一类型。如:Callable[[int], str]
没有指示可选参数或关键字参数的语法,这种函数类型很少用作回调类型。Callable[…, ReturnType]
可用于类型提示一个可调用的接受任意数量的参数并返回ReturnType
。一个普通的Callable
等价于Callable[…, Any]
。
将其它可调用对象作为参数的可调用对象可能表明它们的参数类型使用typing.ParamSpec
相互依赖。此外,如果该可调用对象从其它可调用对象中添加或删除参数,则可以使用typing.Concatenate
运算符。
1 | def other_function(x: int, y: float) -> float: |
Literal
字面类型,可用于向类型检查器指示相应的变量或函数参数与提供的字面量(或多个字面量之一)等效的值。Literal[…]
不能创建子类。在运行时,允许将任意值作为Literal[…]
的类型参数,但类型检查器可能会对此加以限制。
1 | def validate_simple(data: Any) -> Literal[True]: ... # always returns True |
TypeVar
容器中,对象的类型信息不能以Generic
(泛型)方式静态推断,因此,抽象基类扩展支持下标(subscription)
,用于表示容器元素的预期类型。可以使用typing
模块中的TypeVar
新工厂实现Generic
参数化
- 用户定义的类可以定义为
Generic
类 Generic
类型支持多个类型变量,不过,类型变量可能会受到限制Generic
类型变量的参数都必须是不同的Generic
支持多重继承- 从
Generic
类继承时,可以修复一些类型变量 - 使用
Generic
类而不指定类型参数时,每个位置的类型都预设为Any
。
1 | T = TypeVar('T') # Declare type variable, Can be anything |
TypedDict
把类型提示(type hints)
添加到字典的特殊构造(special construct)
。在运行时,它是一个普通的dict
。TypedDict
声明一个字典类型,该类型期望它的所有实例都有一组固定的keys
,其中每个key
都与对应类型的值关联。这种期望不会在运行时检查,而只会由类型检查器强制执行。默认情况下,所有的keys
都必须出现在一个TypedDict
中,可以通过指定总体(totality
)来重写它。
1 | class Point2D(TypedDict): |
Dict
dict
的泛型(generic
)版本,用于注解(annotate
)返回类型。注解参数时,最好使用抽象集合类型(abstract collection type
),例如Mapping
。Dict
与dict
之间没有真正的区别,但是Dict
是泛型类型,它允许你指定key
和value
的类型,使其更加灵活。
1 | def count_words(text: str) -> Dict[str, int]: ... |
Mapping
collections.abc.Mapping
的泛型(generic
)版本。
1 | def get_position_in_index(word_list: Mapping[str, int], word: str) -> int: |
List
list
的泛型(generic
)版本,用于注解(annotate
)返回类型。注解参数时,最好使用抽象集合类型(abstract collection type
),例如Sequence
或Iterable
。
1 | T = TypeVar('T', int, float) |
Tuple
元组类型,Tuple[X, Y]
是二项的元组类型,第一个元素的类型是X
,第二个元素的类型是Y
。空元组的类型可以写为Tuple[()]
。如Tuple[int, float, str]
是由整数、浮点数、字符串组成的三项元组。可用字面省略号(literal ellipsis)
指定可变长元组,如Tuple[int, …]
。
1 | # 指定所有元素的类型 |
Set
builtins.set
的泛型版本,用于注解返回类型。注解参数时,最好使用抽象集合类型,例如AbstractSet
。
1 | x: Set[int] = {1, 2, 3}; print(x) # {1, 2, 3} |