简介
Python是一门弱类型的语言,很多时候我们可能不清楚函数参数类型或者返回值类型,很有可能导致一些类型没有指定方法,在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果,就不得不去阅读代码的具体内容,降低了阅读的速度,typing模块可以很好的解决这个问题。
Python 运行时并不强制标注函数和变量类型。类型标注可被用于第三方工具,比如类型检查器、集成开发环境、静态检查器等。
自python3.5开始,PEP484为python引入了类型注解(type hints),typing的主要作用有:
类型检查,防止运行时出现参数、返回值类型不符。
作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
模块加入不会影响程序的运行不会报正式的错误,pycharm支持typing检查错误时会出现黄色警告。
别名和NewType
1. 类型别名
要定义一个类型别名,可以将一个类型赋给别名。类型别名可用于简化复杂类型签名,在下面示例中,Vector 和 list[float] 将被视为可互换的同义词:
请注意,None 作为类型提示是一种特殊情况,并且由 type(None) 取代,这是因为None是一个存在于解释器中的单例对象。
2. NewType
使用 NewType辅助函数创建不同的类型,静态类型检查器会将新类型视为它是原始类型的子类。
仍然可以对 UserId 类型的变量执行所有的 int 支持的操作,但结果将始终为 int 类型。这可以让你在需要 int 的地方传入 UserId,但会阻止你以无效的方式无意中创建 UserId:
需要注意,这些检查仅通过静态类型检查程序来强制。NewType返回的是一个函数该函数立即返回传递它的任意值这就意味着UserId(1234)并不会创建一个新的类或引入任何超出常规函数调用的开销。
因此,运行过程中same_value is Newtype("TypeName", Base)(same_value) 始终为True。
但是,可以基于 NewType 创建 NewType 。
使用类型别名声明两种类型彼此 等效 。Alias = Original 将使静态类型检查对待所有情况下 Alias 完全等同于 Original。当您想简化复杂类型签名时,这很有用。
相反,NewType 声明一种类型是另一种类型的子类型。Derived = NewType('Derived', Original) 将使静态类型检查器将 Derived当作 Original 的 子类 ,这意味着 Original 类型的值不能用于 Derived 类型的值需要的地方。当您想以最小的运行时间成本防止逻辑错误时,这非常有用。
常用类型
typing模块最基本的支持由 Any,Tuple,Callable,TypeVar 和 Generic类型组成。
1. 泛型集合类型
class typing.List(list, MutableSequence[T])
list的泛型版本。用于注释返回类型。要注释参数,最好使用抽象集合类型,如Sequence或Iterable。
示例:
class typing.Dict(dict, MutableMapping[KT, VT])
dict 的泛型版本。对标注返回类型比较有用。如果要标注参数的话,使用如 Mapping 的抽象容器类型是更好的选择。
示例:
类似的类型还有
class typing.Set(set, MutableSet[T])
2. 抽象基类
class typing.Iterable(Generic[T_co])
要注释函数参数中的迭代类型时,推荐使用的抽象集合类型。
class typing.Sequence(Reversible[T_co], Collection[T_co])
要注释函数参数中的序列例如列表类型时,推荐使用的抽象集合类型。
class typing.Mapping(Sized, Collection[KT], Generic[VT_co])
要注释函数参数中的Key-Value类型时,推荐使用的抽象集合类型。
3. 泛型
class typing.TypeVar
类型变量。
需要注意的是 TypeVar不是一个类使用 isinstance(x, T) 会在运行时抛出 TypeError 异常。一般地说, isinstance()和 issubclass()不应该和类型变量一起使用。
示例:
typing.AnyStr
AnyStr是一个字符串和字节类型的特殊类型变量AnyStr = TypeVar('AnyStr', str, bytes),它用于可以接受任何类型的字符串而不允许不同类型的字符串混合的函数。
class typing.Generic
泛型的抽象基类型,泛型类型通常通过继承具有一个或多个类型变量的该类的实例来声明。
泛型类型可以有任意数量的类型变量,并且类型变量可能会受到限制。
每个参数的类型变量必须是不同的。
可以对 Generic使用多重继承。
从泛型类继承时,某些类型变量可能是固定的。
4. 特殊类型
typing.Any
特殊类型,表明类型没有任何限制。
- 每一个类型都对 Any 兼容。
Any 对每一个类型都兼容。
Any 是一种特殊的类型。静态类型检查器将所有类型视为与Any兼容,反之亦然, Any也与所有类型相兼容。
这意味着可对类型为 Any 的值执行任何操作或者方法调用并将其赋值给任意变量。如下所示,将 Any 类型的值赋值给另一个更具体的类型时,Python不会执行类型检查。例如,当把 a 赋值给 s 时,即使 s 被声明为 str类型,在运行时接收到的是 int 值,静态类型检查器也不会报错
所有返回值无类型或形参无类型的函数将隐式地默认使用Any 类型,如下所示2种写法等效。
Any和 object 的行为对比。与Any相似,所有的类型都是object 的子类型。然而不同于 Any,反之并不成立:object 不是 其他所有类型的子类型。
这意味着当一个值的类型是object 的时候,类型检查器会拒绝对它的几乎所有的操作。把它赋值给一个指定了类型的变量(或者当作返回值)是一个类型错误。比如说,下述代码hash_a会被IDE标注不能从object找到magic的引用错误,而hash_b则不会:
typing.NoReturn
标记一个函数没有返回值的特殊类型。
5. 特殊形式
class typing.Type(Generic[CT_co])
一个注解为 C 的变量可以接受一个类型为 C 的值。相对地,一个注解为 Type[C] 的变量可以接受本身为类的值 。 更精确地说它接受 C的 类对象 ,例如:
注意Type[C] 是协变的:
typing.Tuple
元组类型,Tuple[X, Y] 标注了一个二元组类型,其第一个元素的类型为 X 且第二个元素的类型为Y。空元组的类型可写作 Tuple[()]
为表达一个同类型元素的变长元组,使用省略号字面量,如Tuple[int, ...]。单独的一个 Tuple 等价于 Tuple[Any, ...],进而等价于tuple 。
示例: Tuple[int, float, str]表示一个由整数、浮点数和字符串组成的三元组。
typing.Union
联合类型;Union[X, Y]意味着:要么是 X,要么就是 Y。
定义一个联合类型,需要注意的有:
- 参数必须是类型,而且必须至少有一个参数。
- 能继承或者实例化一个联合类型。
- Union[X, Y]不能写成 Union[X][Y] 。
- 可以使用 Optional[X] 作为Union[X, None]的缩写
- 联合类型的联合类型会被展开打平,比如
仅有一个参数的联合类型会坍缩成参数自身,比如:
多余的参数会被跳过,比如:
在比较联合类型的时候,参数顺序会被忽略,比如:
typing.Optional
可选类型。Optional[X] 等价于Union[X, None]。
typing.Callable
可调用类型;Callable[[int], str]是一个函数,接受一个 int 参数,返回一个str。
下标值的语法必须恰为两个值:参数列表和返回类型。参数列表必须是一个类型和省略号组成的列表;返回值必须是单一一个类型。不存在语法来表示可选的或关键词参数,这类函数类型罕见用于回调函数。Callable[..., ReturnType](使用字面省略号)能被用于提示一个可调用对象,接受任意数量的参数并且返回 ReturnType。单独的 Callable 等价于Callable[..., Any],并且进而等价于 collections.abc.Callable 。
本文介绍了Python的typing模块,主要用于类型注解,包括类型别名、NewType、常用类型如List、Dict、Set、Iterable、Sequence、Mapping等,以及泛型、Any、NoReturn、Callable、Optional等特殊类型,旨在提升代码可读性和静态检查效率。

6679

被折叠的 条评论
为什么被折叠?



