引入

我们在阅读python3写的代码时,会看到类似如下的程序:

def add(x: int, y: int) -> int:
    return x+y

初步看上去,与python2相比,这个函数在定义上,多了3个int。前两个int表示输入数据的类型,"->"符号后面的int,表示返回值类型。

在python中,是不用强制申明变量类型的。python3的这种新语法,貌似可以直接申明变量类型?

函数注解

  1. 函数注解的说明

这种“申明函数参数中变量类型,与返回值类型”的语法,就是python3所谓的“函数注解”[1]。

注解,只是“adding arbitrary metadata annotations to Python functions”,添加的这些“类型申明”,都是额外信息,不是强制要求的。

并且,“函数注解”的目的,是给人看的,不是给解释器看。所以,哪怕类型使用错误,编译器也不会报错,举例如下:

def add(x: int, y: int) -> int:
    return x+y

z = add(3.14, 1.23)

用python3.0以上的版本运行这段程序,并不会报错,即便实参是浮点型,而形参要求是整型。最终z的值是4.37。

Python 解释器并不会因为这些注解而提供额外的校验,没有任何的类型检查工作。也就是说,这些类型注解加不加,对你的代码来说没有任何影响[2]。

“函数注解”的作用,仅仅是:(1)方便程序员查看,(2)IDE的设计者可以用来做代码检查。

  1. 如何访问函数注解

我们如何得到某一个函数的类型注解呢?其实很简单,使用__annotations__就可以,比如上面定义的函数add,运行如下代码:

add.__annotations__

得到的结果就是{'return': int, 'x': int, 'y': int}

结果中能得到返回值与各个参数的类型要求。

变量注解

变量注解,就是“adding syntax to Python for annotating the types of variables (including class variables and instance variables)”,简单来说就是为变量(包括类中的变量与实例变量)添加类型注解。具体用法如下,注意要用python3.6以上的版本运行:

  1. int/str/float变量注解
a1: int # a1为int型变量,没有赋值
a2: int = 123 # a2为int型变量,赋值为123
b1: str # b1为str型变量,没有赋值
b2: str = 'hello'# b2为str型变量,赋值为hello
c1: float# c1为float型变量,没有赋值
c1: float = 3.14# c1为float型变量,赋值为3.14

注意,python的解释器并不会为变量进行真实的“类型检查”,这种注解的作用只是为了方便阅读(或IDE自己来检查),所以如下程序执行是正确的,不会报错:

a1: int 
a2: int = 123 
a1 = 3.14
a2 = 'hello'
  1. list/tuple/dict变量注解
from typing import List, Tuple, Dict

a1: List[int] # a1是一个list,其中的数据都是int类型,没有赋值
a2: List[str] = ['1','2','3']# a2是一个list,其中的数据都是int类型,赋值为['1','2','3']
b1: Tuple[int] # a1是一个tuple,其中的数据都是int类型,没有赋值
b2: Tuple[float] = (1.1,2.2,3.3)# a2是一个tuple,其中的数据都是float类型,赋值为[1.1,2.2,3.3]
c1: [Dict[str, int]] = {} # c1是一个dict,其中key为str类型,value为int型,没有赋值
c2: [Dict[str, int]] = {'a':1, 'b':2, 'c':3} # c1是一个dict,其中key为str类型,value为int型,赋值为{'a':1, 'b':2, 'c':3}

同理,不按注解的类型来,也不会报错。

  1. 如何访问变量注解

在程序中申明的所有注解变量,都可以由__annotations__来访问到,如下:

>>> __annotations__ # 访问所有变量的注解

{'a1': typing.List[int], 'a2': typing.List[str], 'b1': typing.Tuple[int], 'b2': typing.Tuple[float], 'c1': [typing.Dict[str, int]], 'l': typing.List[int], 'c2': [typing.Dict[str, int]]}

静态类型检查模块mypy

既然解释器不会对注解类型不匹配的情况进行报错,那么如何检查类型不匹配的情况呢?

第三方模块mypy就能做类型检查,前提是要做好类型注解。具体举例如下:

  1. 安装mypy
pip install mypy
  1. 写一个test.py程序,如下所示
def add(x: int, y: int) -> int:
    return x+y

z: int
z = 'hello'
z = add(3.14, 1.23)
  1. 用mypy对test.py做类型检查

在CMD里,用命令mypy test.py来检查,可以看到输出报错信息如下:

mypy test.py

test.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int")
test.py:6: error: Argument 1 to "add" has incompatible type "float"; expected "int"
test.py:6: error: Argument 2 to "add" has incompatible type "float"; expected "int"

mypy提示

  • 第5行1个错误,因为z变量被申明为int类型,但却被赋值为str的’hello’
  • 第6行2个错误,因为函数中两个变量都被申明为int,但这里却送入float类型的两个数据

总结

python3.0 引入了函数注解,python3.6 引入了变量注解。以后这种语法可能会变,但笔者写作时,在python3.7上实验本文所述,是正确的。

它们都让python越来越像C/C++了,将来python是否还会引入泛型呢?这样python就越来越像Java了。。。

参考

  • [1] PEP 3107 – Function Annotations. https://www.python.org/dev/peps/pep-3107/
  • [2] https://zhuanlan.zhihu.com/p/37239021
  • [3] PEP 526 – Syntax for Variable Annotations. https://www.python.org/dev/peps/pep-0526/
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐