说明
Python 教程正在编写中,欢迎大家加微信 sinbam 提供意见、建议、纠错、催更。
简单说,Python 类型注解功能可以让我们的代码更加易读,从而达到编写更加健壮的代码目标。类型注解又叫类型暗示,将函数、变量声明为一种特定类型。当然,它并不是严格的类型绑定,所以这个机制并不能阻止调用者传入不应该传入的参数。
背景
由于 Python 是一个动态语言,定义和使用变量时不需要申明变量的数据类型即可使用,但这样也会带来一定的问题,如阅读代码时不知道数据是什么类型,调用时不小心会传入错误的数据类型。因此,Python 的类型注解功能就显得比较重要了。
一个字符和数字类型是无法相加的,否则会报以下错误:
a = 1
b = '2'
a + b
# TypeError: unsupported operand type(s) for +: 'int' and 'str'
调用者在使用函数时如果没有完善的文档,不知道要传入的数据类型分别是什么,同时文档也很难表达复杂的数据类型。有了类型注解可以让 IDE 知道了数据类型后,更加准确地进行自动补全。有了类型注解可以提供给第三方工具,做代码分析,发现隐形bug。函数注解的信息,保存在 __annotations__ 属性中可以来调用。
在 Python 3.5 中,Python PEP 484 引入了类型注解(type hints),在 Python 3.6 中,PEP 526 又进一步引入了变量注解(Variable Annotations)。
基本方法
以下是一个使用示例:
# 定义一个变量
x: int = 2
x + 1
# 3
# 定义一个除法函数
def div(a: int, b: int) -> float:
return a/b
# 查看类型注解信息
div.__annotations__
{'a': int, 'b': int, 'return': float}
语法注意的点:
变量类型:在变量名后加一个冒号,冒号后写变量的数据类型,如 int、dict 等
函数返回类型:方法参数中如变量类型,在参数括号后加一个箭头,箭头后返回值的类型
格式要求(PEP 8,非强制):变量名和冒号无空格,冒号和后边类型间加一个空格,箭头左右均有一个空格
提示性
但值得注意的是,这种类型和变量注解实际上只是一种类型提示,对运行实际上是没有影响的。
def add(a: int, b: int) -> str:
return a + b
# 查看返回类型
type(add(1, 2))
# int
比如上例,调用 add 方法的时候,我们注解返回一个字符串,但它返回的仍然是 int,也不会报错,也不会对参数进行类型转换。
不过有了类型注解,一些 IDE 是可以识别出来并提示的,比如 PyCharm 就可以识别出来在调用某个方法的时候参数类型不一致,会提示 WARNING。如类似以下字典嵌套列表类型的提示:
'''
Expected type 'Mapping[str, str]',
got 'Dict[str, Union[List[str], List[Union[bool, str]]]]' instead
'''
类型定义方法
以下几种类型的定义方法:
None
内置的类(int, str,float)
标准库(typing 模块)
第三方扩展库
抽象出的基类
用户定义的类
类别名 type aliases(s = str,然后用 s)
使用 NewType
使用 own generic types
更多见 typing 中的构建方法。
复杂结构
对于一些结构的数据类型就不能用上述简单的方法表示了,比如:
names: list = ['lily', 'tom']
version: tuple = (6, 6, 6)
operations: dict = {'sad': False, 'happy': True}
以上虽然定义了最外层的数据结构,如列表、元组、字典,但他们每个元素的类型是什么也没有定义清楚,因此需要在 typing 库的帮助下来定义准确内部的结构:
from typing import List, Tuple, Dict
names: List[str] = ['lily', 'tom']
version: Tuple[int, int, int] = (6, 6, 6)
operations: Dict[str, bool] = {'sad': False, 'happy': True}
再如:
from typing import Union, List, Tuple, Dict
config: Dict[str, Union[List[str], Tuple[bool, str]]]= {
'width': ['100%', 'Width of img'],
'height': ['auto', 'Height of img'],
'fluid': (True, '外层包含一个div')
}
config 变量存在着复杂的嵌套结构,以上注解就声明了它的结构,方便调用者来使用。
typing 库是 Python 内置的类型标注标准库,用它可以来解决这些问题,它可以实现复杂的类型注解工作,可以查看 typing 教程。
lambda 的类型标注
由于类型注解的语法和 lambda 的语法冲突,因此不能直接对 lambda 做类型注解,但我们可以将 lambda 传给一个变量,通过对这个变量做 lambda,达到相同的目的。以下对 lambda 的几个例子:
from typing import Callable
# is_even 传入 int 返回布尔
is_even: Callable[[int], bool] = lambda x: (x % 2 == 0)
# func 传入两个字符串,返回 int
func: Callable[[str, str], int] = lambda var1, var2: var1.index(var2)
强制类型检测
inspect 模块是 Python 内置的类型检查标准库,它提供了一些有用的函数帮助获取对象的信息,例如模块、类、方法、函数、回溯、帧对象以及代码对象。例如它可以帮助你检查类的内容,获取某个方法的源代码,取得并格式化某个函数的参数列表,或者获取你需要显示的回溯的详细信息。
查看 inspect 教程(todo)。
可以通过 mypy 库来检验最终代码是否符合注解:
#安装 mypy
pip install mypy
# 执行代码
mypy test.py
如果不符合标注的类型要求会报错。
参考
https://www.python.org/dev/peps/pep-3107/
https://www.python.org/dev/peps/pep-0484/
https://www.python.org/dev/peps/pep-0526/