十二年的时间 Python 官方是怎么把一个特性,变成标准,再把标准变成生产力的。
缘起
Python 是一门弱类型的语言,弱类型就是它原罪。因为弱类型的存在使得一个对象的类型到运行时才确定的下来,一方面这给了 Python 程序巨大的灵活性。另一方面它也有它的问题,像下面这个函数,鬼知道它要接收什么类型的参数,返回什么类型的值。
def fun(a):
... 省略
对于这种无法确认参数类型、返回值类型的情况在 Java 中就不可能发生。也就是说 Python 相比 Java 来讲更加依赖于文件,不然参数都不知道怎么传递和接收。
2006 以前
在这个历史时期,由于相关的配套都还没有,工程师们通常是把 API 的使用文档,写在文档字符串中。由于字符串的格式是任意的,所以我们看到文档注释也是千奇百怪,其中又以 numpy 这个项目的文档字符串写的最好,大家都参考它的写,就有了 “ numpy 风格的文档字符串一说 ” 下面我们来看一段代码。
def greeting(name):
"""
在参数 name 的前面加上 'hello ' 前缀并返回
Parameter
---------
name:str
要问好的名字
Return
------
str
Example
greeting('tim')
-------
"""
return 'hello ' + name
numpy 的这个写法有一个问题,那就是如果把文档注释里面的各个节都写完,非常有可能搞的文档比代码长。
2006 年
这一年 PEP-3107发布,官方在语法上做了增强(函数注解),允许我们在定义函数的时候通过一个表达式来说明参数、和返回值的意义。语法上看起来像下面这样。
def fun(identifier[:expression][=expression]) -> expression:
现在我们用新语法改造一下我们之前的代码,在表达式中说明一下参数的类型。
def greeting(name:str)->str:
return 'hello ' + name
由于语法上定义的是接受一个表达式,所以我们在这里定上参数的数据类型;由于字符串字面值也是表达式,所以这里我们也可以填写一个与业务逻辑相关的字符串。
def greeting(name:'名字')->'加了前缀之后的名字':
return 'hello ' + name
现在可以看出问题了,由于可以填写字符串,那这个随意度就比较大了,针对同一个参数它的文档完全可以写成各种不同的样子。这一时期函数的文档就更加混乱了,终结这一乱世的解决方案还要再过八年。
2014 年
八年过去了官方发现大家对于函数注解的使用都停留在声明参数和返回值的类型上,单个类型表达式无法满足的情况下就写字符串。
PEP-484 Type-Hints 发布,官方推出一套专门用于函数注解的类型系统,并允许用户自定义函数注解类型,再加上 2006 年发布 PEP-3107 中定义的 __annotations__ 到这里函数注解就有了标准的写法和标准的读取 API 了。
from typing import TypeVar
UserName = TypeVar(str)
def greeting(name:UserName)->str:
return 'hello ' + name
到了这一步是可以做到针对一个特定的问题(函数注解)只会有一个最优的解决方案,但是这个一直推动不下去。如果我们通过历史的后视镜看问题的话,答案还是比较明显的;那就是 Type-Hints 虽然在文法上做到了和谐统一,但并不能简化我们的开发工作,要统一风格还要改老代码,这样大家自然就不怎么动了。
想要通过他们提升生产力还要再过四年。
2018 年
Python-3.7 为了提升生产力,在类型提示上面再加了一把功夫,带来了 dataclasses 。假设我们现在要定义一个 Person 类,实例之间可以通过 age 属性来比较大小。用现在的新写法可以简单到爆炸。
#/usr/bin/env python3
# -*- conding:utf -*-
from dataclasses import dataclass,field
@dataclass(order=True,repr=True)
class Person(object):
name:str=field(compare=False)
age:int
if __name__ == "__main__":
tom = Person('tom',16)
tim = Person('tim',100)
print(f"{tom} < {tim} = {tom < tim}")
运行效果
python3 main.py
Person(name='tom', age=16) < Person(name='tim', age=100)
官方的参数文档
PEP 3107 函数注解
https://www.python.org/dev/peps/pep-3107/
PEP 484 类型提示
https://www.python.org/dev/peps/pep-0484/
PEP 557 dataclasses
https://www.python.org/dev/peps/pep-0557/
dataclass 标准库模块
https://docs.python.org/3.8/library/dataclasses.html