? “Python猫” ,一个值得加星标的公众号
花下猫语:众所周知,Python 是一门动态类型语言,这也是造成它性能较慢的一大原因。如今 Python 也引入了一些类型检查的辅助,那么,类型检查对于提升 Python 代码健壮性,有没有帮助呢?(既然这么问了,那肯定是有的……)
作者:无与童比(Python/Golang/JS, 全干(栈)工程师)
原文:https://zhuanlan.zhihu.com/p/56863684
图 | 《借东西的小人阿莉埃蒂》剧照
本文是《提升你的 Python 项目代码健壮性和性能》系列的第一篇文章。
本系列仅仅从笔者的项目经历来讲解一些提升代码健壮性的姿势和小技巧。
本文目录如下:
▼ 0x00 前言 : section
▼ 0x01 Gradual Typing : section
静态类型 VS 动态类型 : section
Gradual Typing = 静态类型 + 动态类型 : section
▼ 0x02 Python Typing 实战 - MyPY : section
MyPy : section
快速入门 : section
▼ 0x03 常见问题 : section
如何忽略 mypy 警告 : section
循环导入 : section
0x04 Typing Anotation 项目最佳实践 : section
▼ 0xEE 参考 : section
PEP : section
扩展文章 : section
当我刚知道 Python 要添加类型的时候,我的内心是拒绝的。
但是,尝试了俩个疗程之后,腰也不疼了,腿也不疼了,走起路来都有劲了,嗯,真香。
为啥需要 Type Annotation?
因为软件开发需要协作,动态类型给人极大的灵活性,写的时候很爽,但如果解放了双手,撸起袖子一通写,自己写起来爽了,自己重构的时候或者其他人来看代码的时候,头发就会加速掉落。
加了 Typing 能解决这个问题嘛?不能,但适当的使用可以极大的提升代码的健壮性。
在如下的场景中,Typing 可以发挥作用
这样大大减少了代码量上来之后的类型检查不足带来的返工问题。
在你刚入门一门编程语言的时候,我们常常说,Java 是强类型(静态类型)语言,Python 是弱类型(动态类型)语言
从这两位诞生开始,静态类型和动态类型就一直进行旷日持久的圣战。
然而,而现在的发展趋势是:
什么是 Gradual Typing?
Gradual typing 允许开发者仅在程序的部分地区使用 Annotate/Type. 即,既不是黑猫(静态), 也不是白猫(动态),从而诞生了熊猫(动静结合)。
话说回来,要知道为什么这么搞,首先要知道动态类型和静态类型会给程序开发带来什么优势和劣势。
静态类型的语言,比如在写 Java 的时候,如果你把一个 int 赋值给了 string 的变量,IDE 会通过类型检查器立即报错并告诉你,你这个值赋值错啦。这个就是 Java 程序的检查阶段。
动态类型的语言,比如在写 Python 的时候,如果不用一些额外的手段,这种低级的错误,并不会在检查时爆出来,只会在运行时爆出来。如果线上还是出这个问题,就蛋疼了。
为了进行友好的讨论,本人将精分成 Javaer 和 Pythonist, 通过两人对话的方式,来讨论类型。
再比如说,
LeetCode 上面有一道题目,叫做最长连续 1
Input 是 [1,1,0,1,1,1] Output 是 3
我们尝试用 Python 来看下
def find_max_consecutive_ones(num):
return max(map(lambda x: len(x), ''.join([str(num) for num in nums]).split('0')))
我们尝试用 Java 来看下
public class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int result = 0;
int tmp = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0)
tmp = 0;
else {
tmp += 1;
result = Math.max(tmp, result);
}
}
return result;
}
}
Java 创始人的头发
Python 创始人的头发
旁白君:有道是,梅须逊雪三分白,雪却输梅一段香。
Gradual Typing 就是在动态语言的基础上,增加了可选的类型声明 (Type Annotation)
这对于我这种人是福音,
对于我个人而言,我是希望 Python 是有类型的
但我又不希望这个声明不是强制性的
我在构思程序的时候,想专注于接口的设计。在落实编码并且把代码写的足够的 dry 之后,在被调用的一些地方加上类型声明,这样可以提升我写代码的速度。
mypy 是一个可选的静态分析器,官网介绍上说,mypy 将使你的程序更加易懂,调试和维护。
这个程序
Dropbox 的团队开发,Guido van Rossum 领导开发
本小节部分摘录 Type hints cheat sheet
建议读者收藏原网址 https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html
# 内置类型
x: int = 1
x: float = 1.0
x: bool = True
x: str = "test"
x: bytes = b"test"
child: bool
if age < 18:
child = True
else:
child = False
# 普通函数
def stringify(num: int) -> str:
return str(num)
# 生成器
def f(n: int) -> Iterable[int]:
i = 0
while i < n:
yield i
i += 1
直接看起来似乎,加不加 typing 对现在的代码改善并不是很明显嘛。
我们可以给复杂类型起别名:
比如:
def f() -> Union[List[Dict[Tuple[int, str], Set[int]]], Tuple[str, List[str]]]:
def b() -> Union[List[Dict[Tuple[int, str], Set[int]]], Tuple[str, List[str]]]:
AliasType = Union[List[Dict[Tuple[int, str], Set[int]]], Tuple[str, List[str]]]
def f() -> AliasType:
...
def b() -> AliasType:
...
看起来还行,但还是没有感觉到很明显的代码质量改善。
好,再看一例,使用 ClassVar 禁止属性无法在实例上设置
from typing import ClassVar
class A:
x: ClassVar[int] = 0 # Class variable only
A.x += 1 # OK
a = A()
a.x = 1 # Error: Cannot assign to class variable "x" via instance
print(a.x) # OK -- can be read through an instance
举个例子,flask-sqlalchemy, 可以通过 YouModel.query.get(id) 来拿到 YouModel 的实例,但 IDE 不能推断出这个实例是什么。
# 方法一,Cast
you_model_ins: YouModel = YouModel.query.get(id)
# 方法二,包装一下 get 方法
class YouModel(base):
def get(id) -> "YouModel": # 注意这里的字符串
pass
you_model_ins = YouModel.get(id)
细心的读者可能看到这里的 YouModel 的返回值类型居然使用了 YouModel 的字符串,如果是 Java 的话,是可以直接写 YouModel 的。
# 加上类型延迟求值
from __future__ import annotations
class YouModel(base):
def get(id) -> YouModel:
pass
you_model_ins = YouModel.get(id)
还有其他的用法,请参考 MyPY 的官方文档
有的地方的代码不进行检查的话会方便很多。
与 flake8 类似,在注释后面写上标志就可以忽略了。
youcode # type: igonre
我现在有两个文件,一个是 user.py 另一个是 order.py
在 user 里面有个方法需要返回 order 里面的 Order 列表,order 里面有个 order.owner 需要返回 User 实例。
如果不用类型声明的话,在 user 需要 order 的时候 import 进来即可规避循环导入。
在使用类型声明之后,建议在 user 里面这么写
if TYPE_CHECKING:
from project.models.order import Order # noqa
通过本文了解了基本的 Typing Anotation 的用法,其实效果还不够,本着对爱学习的读者老爷的负责的态度。
所谓『纸上得来终觉浅,绝知此事要宫刑』, 哦不『躬行』
推荐一个超级牛的大项目来让大家了解一下 typing annotation 的最佳实践。
https://github.com/zulip/zulip/
当然,从这个项目里面不仅仅能学到 typing annotation, 还能学到大项目下,牛 X 的公司的做法
有机会的话,我会挑其中的一小部分讲解一下。