Python和大多数的解释型语言一样,属于弱类型的语言。它的优点就是语言灵活,不需要考虑各种类型以及模板。但是缺少类型检查也会引入很多问题。比如,

def join(string_list):
    return ', '.join(string_list)

对于这个函数,期望的结果就是会把[‘hello’, ‘world’]变成’hello, world’。 但是如果不小心没有传list只是传了一个字符串’hello’,这段代码也不会报错,只是会返回’h, e, l, l, o’这个并不期望的结果。简单的程序没有什么大问题,如果这段代码在大程序里面隐藏的非常深,那debug成本会非常的高。这也就是为什么现在对动态语言进行静态类型检查这么流行,比如著名的TypeScript。

其实Python3从很早开始起,就逐步支持静态类型检查了,比如说,上面的代码可以写成

from typing import List

def join(string_list: List[str]) -> str:
    return ', '.join(string_list)

这样声明函数有一个好处,就是再也不需要在注释里面说明变量类型了,更加直观。但是这个时候下面的代码依旧可以运行

>>> join('hello')
'h, e, l, l, o'

这个是因为Python只是把这种类型的声明看成是一种对函数的注解(annotation),而注解本身并不具有任何的意义,也不影响运行的过程。与没有注解的版本差别就是多了一个__annotations__的字段

>>> join.__annotations__
{'string_list': typing.List[str], 'return': <class 'str'>}

那么静态类型检查怎么实现呢?Python对于注解语法的设计,就是把静态的类型检查与代码的解释运行分开,静态类型检查可以使用第三方库去做, CPython解释器无需做任何事情。就好比TypeScript,静态类型检查以及转换成js是它的工作,与js的解释运行没有关系。

目前,基于注解(annotation)的Python静态类型检查库有很多,比如Guido van Rossum的mypy以及google的pytype。在这里简单介绍一下mypy。mypy安装非常容易,使用pip即可,但是注意这个包的名字叫做mypa-lang而不是mypy,在Ubuntu 16.10下

$ pip3 install mypy-lang

在安装成功后,对于下面代码test_mypy.py

from typing import List

def join(string_list: List[str]) -> str:
    return ', '.join(string_list)

print(join('hello'))

运行以及使用mypy检查

$ python3 test_mypy.py
h, e, l, l, o
$ python3 -m mypy test_mypy.py
test_mypy.py:6: error: Argument 1 to "join" has incompatible type "str"; expected List[str]
$

就可以找到第六行print(join('hello'))的类型错误, 而把test_mypy.py改成

from typing import List

def join(string_list: List[str]) -> str:
    return ', '.join(string_list)

slist = ['hello', 'world']
print(join(slist))

运行mypy检查

$ python3 test_mypy.py
hello, world
$ python3 -m mypy test_mypy.py
$

就可以成功的通过。

除了上面对函数的注解,其实Python3的annotation还有非常多的功能,比如对于类的注解,或者泛型,或者在Python 3.6中还有对变量的注解。如果发现Python代码越开越多,调试越来越麻烦的时候,不妨可以试试使用这个。