10 月 9 日,Python 3.15.0 正式发布。这个版本由 1,012 位贡献者提交了 5,643 次改动,发布经理是 Hugo van Kemenade,背后有 Sovereign Tech Agency 的赞助。版本号一出来,社区讨论的焦点没放在新语法糖上,而是绕回一个老问题:Python 启动慢。
慢到什么程度?一个稍微像样的 CLI 工具,比如数据团队内部用的命令行,依赖 pandas、SQLAlchemy 和一堆内部 SDK,光 import 完依赖再走到真正的业务代码,往往要两三秒;在 serverless 函数里,这两三秒直接算进冷启动账单。原因在 import 机制本身:每 import 一个模块,解释器都要定位文件、从磁盘读进来、编译成字节码、执行一遍顶层代码。依赖树深的项目,哪怕只用到其中一小部分,整棵树都得过一遍。
Python 社区想动这块已经很久了。上一次是 PEP 690,方案走的是隐式路线——默认把所有 import 都改成惰性的,语言层面一刀切。讨论了很久,2024 年被否掉,理由很现实:隐式全局改动和现有无数库的假设冲突,出了难查的 bug 责任算谁的。
这次的 PEP 810 走了另一条路:显式、opt-in。新语法长这样:lazy import foo。开发者自己决定哪个 import 要惰性,惰性只作用于这一句 import,不级联到它依赖的其他模块。真正用到 foo 的那一刻,才去执行那个模块。改动面小,行为可预测,兼容性风险也压到了局部。三年前社区吵的那套“全局隐式”方案,被一个“你自己挑”的方案替代了。
对类型检查器和静态分析工具来说,显式写法还有个附带好处:工具能直接看出哪些 import 是故意惰性的,不会把它误判成未使用的导入。这也是 PEP 810 相比 PEP 690 更容易被生态接受的原因之一——它没要求所有工具一起改行为。
机制上值得多讲一句。lazy import foo落地后,解释器拿到的是一个延迟绑定的代理对象,首次访问foo.某属性、或者把 foo 当参数传进函数、或者对它做dir()这类操作时,才真正去执行那个模块的顶层代码。模块一旦加载完,后续访问就走正常路径,和普通 import 没有差别。这意味着它不会改变现有代码的语义,也不会和循环 import 那套老问题冲突——以前因为顶层 import 触发的循环依赖,改成惰性之后反而能顺手绕开一部分,代价是出错时的堆栈会多一层代理帧,调试时认一下就行。
除了启动速度,3.15 还塞了几个用得上的小东西。PEP 814 把frozendict做成了内置类型——不可变的字典,以前大家得自己装第三方包或用 MappingProxyType 凑合;PEP 661 加了sentinel内置类型,专门替代None当“未设置”的占位符,避免 None 本身也是合法值时的歧义;PEP 798 允许在推导式里做解包。
性能侧还有一笔账。3.13 开始试水的实验性 JIT,在 3.15 里做了明显升级:官方给的数字是 x86-64 Linux 上几何平均快 7% 到 8%,AArch64 macOS 上对比尾调用解释器快 11% 到 12%。JIT 还在实验阶段,没默认开,方向是把 3.13 那批改动往生产可用推。同版本里 PEP 831 把 frame pointers 默认打开了,系统级 perf 这类工具采样 Python 栈能采得更准;macOS 官方安装包默认带上 free-threading 支持,Windows 64 位官方二进制切到了尾调用解释器。
另外几条容易被忽略的改动顺手提一下。PEP 829 引入包启动配置文件,允许在包级别声明默认的解释器行为,省去在 Dockerfile 或启动脚本里堆一堆环境变量;PEP 686 把 UTF-8 设为默认编码,绕开 Windows 上历史遗留的 GBK 默认编码坑,处理中文和第三方库字符串交互时少踩很多雷。类型系统侧,PEP 728 给 TypedDict 加了类型化的额外字段,PEP 747 用TypeForm标注类型本身,这类改动对写框架和类型检查器的人影响直接,业务代码感知不强。
生态反应比版本号来得还快。10 月 8 日,Pydantic 发 v2.14,同一天宣布支持 Python 3.15 的 lazy imports 和 frozendict。这类核心库的跟进速度,基本决定了一个新 Python 版本什么时候能被业务项目放心升。
这次发布还有个小彩蛋。老贡献者 Barry Warsaw 写了个 TUI 冒险游戏 whatsnewt,玩家在解释器里走“启动大厅”到“发布大门”,18 个谜题对应的都是 3.15 的真实新特性,得真写代码、真跑测试才能解开,类型检查环节还会调 pyrefly 验答案。装法一行命令:uvx --python 3.15 whatsnewt。
回到为什么值得现在翻一翻。3.15 不是那种带 breaking change 的大版本,它真正传递的信号是:Python 团队在性能和开发者体验两件事上,选择了偏保守、可持续的路线。lazy import不替你做决定,JIT 不默认开,free-threading 先在 macOS 包里默认带上观察。对日常写业务的人来说,今年年底到明年上半年,大概率会看到一批基础库陆续声明“已支持 3.15”,那时候再升级也不迟;对维护 CLI 工具、Serverless 函数、打包分发场景的人,lazy import这个语法现在就可以在测试分支里试起来了。具体做法不复杂:把脚本里启动路径上用不到、却被顺手抄进来的那几个重依赖,改成lazy import,跑一遍冷启动计时,数字往往立竿见影。
本文为行业科普分享,所涉观点仅代表作者个人理解,不构成任何投资、消费、医疗建议。文中提及的产品、地点、数据均来自公开渠道或作者亲身体验,或随时间变化。读者据此操作风险自担。