Python的typing模块杂记
typing模块概论
Python是一门动态语言,如果不加上注释的话,很难在阅读阶段去搞清楚函数参数类型或者返回值类型,Python的typing模块可以很好的解决这个问题。
Python 运行时并不强制标注函数和变量类型。类型标注可被用于第三方工具,比如类型检查器、集成开发环境、静态检查器等。去进行语法分析,智能提示等目的使用。但标注上函数和变量类型,有利于开发者阅读代码,所以最好都使用上。
PEP483开始引入了类型提示理论(The Theory of Type Hints)。PEP484则正式引入类型提示。
typing模块的主要作用有:
- 类型检查,防止运行时出现参数、返回值类型不符。
- 作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
- 模块加入不会影响程序的运行不会报正式的错误,pycharm支持typing检查错误时会出现黄色警告。
typing模块的一些具体技术
类型别名(type alias)
要定义一个类型别名,可以将一个类型赋给别名。类型别名可用于简化复杂类型签名,在下面示例中,Vector
和 list[float]
将被视为可互换的同义词:如下面的代码所示:
1# type_alias_sample.py
2
3Vector = list[float] # 这表明,Vector是一个列表,且里面的元素都是float类型
4
5# scalar:float 表示参数是参数应是float类型
6# vector:Vector 表示参数是参数应是列表类型,且里面的元素float都是float
7def scale(scalar:float,vector:Vector) -> Vector:
8 return [scalar * num for num in vector] # 将列表vector中的每个元素取出,和scalar相乘,然后再存进一个列表中,最后返回
9
10v = [1.0, -4.2, 5.4]
11print(v)
12new_vector = scale(2.0, v) # 类型检查器会敲定这个浮点数列表就是Vector类型
13print(new_vector)
14
NewType辅助类
使用 NewType
辅助类,创建不同的新类型,静态类型检查器会将新类型视为它是原始类型的子类。如下面的代码所示:
1# new_type_sample.py
2
3from typing import NewType # 显式地从typing模块中导入NewType辅助类
4
5# 定义一个新类型UserId,且该类型是继承自int类型
6UserId = NewType('UserId', int)
7
8 # 声明函数的参数类型是UserId,返回值类型是字符串
9def get_user_name(user_id: UserId) -> str:
10 return str(user_id)
11
12# 执行类型检查,显式地声明整数42351是一个UserId
13user_a = get_user_name(UserId(42351))
14print("user_a id is "+user_a)
15
16# 执行类型检查,整数-1不是一个UserId,但执行get_user_name函数也并不会出错
17user_b = get_user_name(-1)
18print("user_b id is "+user_b)
在运行时,NewType
语句将生成一个可调用对象,它会立即返回传递给它的任何参数。 这意味着NewType
语句并不会创建新类,也不会引入比常规函数调用更多的开销。如果使用class
语句去继承这个新定义的UserId
,例如下面的代码:
1class AdminUserId(UserId):
2 pass
在运行时会报错。但如果继续使用一个NewType
语句去“继承自”UserId
:
1from typing import NewType
2UserId = NewType('UserId', int)
3ProUserId = NewType('ProUserId', UserId)
则是可以正常运行的。
TypeVar辅助类
使用TypeVar辅助类,可以定义一个“泛型类型”,这个泛型类型既可以指代一切类型,也可以特指某几种类型。例如下面的代码所示:
1#type_var_sample.py
2
3from typing import TypeVar # 显式地从typing模块中导入TypeVar辅助类
4
5T = TypeVar('T') # 定义一个“泛型类型”T,这个类型可以指代一切
6A = TypeVar('A',str,tuple) # 定义一个“泛型类型”A,这个类型可以只可以指代str类型和tuple类型
7
8# 返回一个带n个重复元素x的列表,列表中元素的类型是可以任意类型
9def repeat(x: T, n: int) -> list[T]:
10 return [x]*n
11
12list_1 = repeat("abc",5)
13print("list_1 is ",list_1) # 打印结果 list_1 is ['abc','abc','abc','abc','abc']
14list_2 = repeat((1,2,3),4) # 打印结果 list_2 is [(1,2,3),(1,2,3),(1,2,3),(1,2,3)]
15print("list_2 is ",list_2)
16list_3 = repeat({1:"one",2:2,"three":3},3)
17print("list_3 is ",list_3) # 打印结果 list_3 is [{1:'one',2:2,'three',3},{1:'one',2:2,'three',3},{1:'one',2:2,'three',3} ]
18
19# 比较两者中元素个数,返回元素个数较多的那个输入参数
20def longest(x:A,y:A) -> A:
21 if len(x) >= len(y):
22 return x
23 else:
24 return y
25
26print(longest("abc",('a','b'))) # 打印结果abc
27print(longest("abc",('a','b','c','d'))) # 打印结果('a','b','c','d')
在运行时,isinstance(x, T)
将引发 TypeError
。通常,isinstance()
和issubclass()
不应与类型一起使用。通过查看PEP484可以获取更多细节。