作为 Python 的大用户之一, Dropbox 公司内部聚集了数百万行 Python 代码,动态类型的存在让代码越来越难以理解。因此,公司开始利用 mypy 逐步将代码转换为静态类型。虽然效果得到了充分验证,但整个过程充满了各种错误和失败。本文,Dropbox 公司完整输出了从项目研究到实践的 Python 静态检查全过程,以期对各位开发者有所帮助。
Python资源共享群:626017123
事实上,Python 已经成为 Dropbox 公司使用范围最广的语言,其广泛适用于后端服务与桌面客户端应用程序等(当然,Dropbox 公司也在大量使用 Go 、TypeScript 以及 Rust 等语言)。在 Dropbox 公司数以百万计的 Python 代码行中,动态类型的存在让代码越来越难以理解,并严重影响生产力水平。为了缓解这一问题,Dropbox 公司一直在利用 mypy 逐步将代码转换为静态类型(顺带一提,mypy 可能是目前 Python 当中最流行的独立类型检查器,属于开源项目,其核心开发团队来自于 Dropbox。)。
截至目前,Dropbox 已经在成千上万个项目当中使用 mypy,而且效果都得到了很好地验证。但对于此次全方位检查 Python 代码,Dropbox 仍然抱着忐忑的心情,整个过程也充满了错误与失败。在今天的文章中,Dropbox 将向大家分享 Python 静态检查之旅——从最早的学术研究项目,到现在逐步让类型检查与类型提示成为 Python 社区中众多开发人员的常规操作。现在,已经有多种工具支持类型检查功能,包括各类 IDE 与代码分析器等。
为什么要进行类型检查?
如果开发者只使用过动态类型的 Python,当然有可能对静态类型以及 mypy 感到陌生。甚至,不少开发者就是因为动态类型而喜欢上 Python,但这事儿在逻辑上就有点莫名其妙。其关键应该在于,静态类型检查是实现规模化的前提: 项目越大,需要的静态类型就越多。
一旦项目中包含成千上万行代码,而且有多位工程师在同时使用,以往开发经验告诉我们,理解代码内容就成了保障开发人员工作效率的关键所在。如果没有类型注释,基本的代码作用推理(例如找到函数的有效参数,或者可能的返回值类型)就会成为一大难题。以下是几个在缺少类型注释时,开发人员难以回答的典型问题:
- 这个函数能返回 None 吗?
- 这里的 items 参数是干什么用的?
- id 属性是什么类型:到底是 int、str、抑或是自定义类型?
- 这个参数需要的是一份清单、一个元组还是一个组?
只要有了类型注释,开发者能够很轻松地回答这些与代码片段相关的问题,例如:
复制代码
classResource: id:bytes ... defread_metadata(self, items:Sequence[str]) -> Dict[str, MetadataItem]: ...
- read_metadata 并不会返回 None,因为返回类型不是 Optional[…]
- items 参数代表一系列字符串,其不可能随意迭代。
- id 属性为字节字符串。
在理想情况下,我们当然希望把这一切都记录在文档中,但拥有从业经验的开发者肯定知道没这么好的事儿。即使存在此类文档,我们也无法完全信任其中的内容——例如内容含糊不清或者不够准确,因此带来巨大的误解空间。对于大型团队或代码库,这类问题可能产生巨大的影响:
虽然 Python 在项目早期与中期阶段表现良好,但当项目发展到特定阶段后,