我没想到 Python 3.15 会有这么大的变化

每次 Python 发布新版本,都声称自己更快、更干净、更智能、更符合人体工学,诸如此类。

大多数时候,这些变化要么极其小众,要么技术上令人印象深刻,但与人们实际编写 Python 的方式无关。

Python 3.15 感觉不一样。

不是因为每个功能都具有革命性。有些功能说实话早就该有了。有些感觉像是之前实验的收尾工作。还有一些只有语言极客才会关心。

但这是近期第一个有多个变化能立即影响日常开发工作的 Python 版本。

单是惰性导入(lazy imports)就会改变许多大型 Python 应用的启动方式。

有趣的是,Python 3.15 还回滚了 Python 3.14 最大的变化之一。

这很能说明 Python 开发文化的特点。他们发布了增量式垃圾回收器,人们抱怨内存占用过高,现在它又没了。

很好。

这比假装每个实验性的运行时改动都暗藏玄机要健康得多。

1、惰性导入早就该存在了

这可能是这个版本中最实用的功能。

Python 的启动时间一直有个奇怪的问题:导入半个生态系统就像在你的代码运行之前先交税。

你导入一个框架。那个框架导入六个工具模块。那些模块再导入二十个。然后你的 CLI 工具花了两秒钟才打印出 --help

人们多年来一直手动解决这个问题

if expensive_feature_enabled:
    import pandas

或者

def do_something():
    import torch

不是因为代码看起来好看。而是因为启动延迟很重要。

现在 Python 官方支持惰性导入了。

模块只有在真正使用时才会加载,而不是在启动时。而且重要的是,现有的导入通常可以在不将代码库重写成某种诅咒般的依赖迷宫的情况下变成惰性的。

这比语法本身更重要。

这里的真正价值在于

  • CLI 工具
  • 开发者工具
  • 大型 Web 应用
  • 具有荒谬导入树的 AI 技术栈
  • 基本上每个不小心变成了操作系统的 Python 项目

如果你的代码本来行为就正确,那么它的缺点也 surprisingly 少。

不过说实话:一些项目会发现它们对导入副作用的依赖比想象中要多。

Python 开发者喜欢假装导入只是声明。很多导入实际上是戴着假眼镜的初始化脚本。

2、JIT 终于在基准测试中变得可见了

Python 在 3.13 中引入了 JIT。

当时的反应大多是

"好吧。酷。所以呢。"

因为速度提升很小且不一致。

这未必是失败。最初的版本显然是基础设施工作。但人们听到 "JIT 编译器" 时,期待的是戏剧性的变化。

Python 3.15 是第一个改进开始看起来真实的版本。

据报道,根据工作负载和平台的不同,几何平均改进约为 8%–13%。

不,这不会突然把 Python 变成 Rust。

但在如此广泛使用的语言中,自动获得 10% 的速度提升也不算小。

特别是因为大多数 Python 性能工作通常涉及

  • 用 C 重写热点路径
  • 将所有东西向量化到 NumPy 中
  • 到处添加缓存层
  • 假装 asyncio 解决了架构问题

如果 CPython 能继续让普通 Python 更快,而无需生态系统重写,那就很重要。

有趣的部分在于,JIT 的改进不是单一的技巧。它是一系列运行时优化

  • 追踪改进
  • 更好的机器代码生成
  • 寄存器分配
  • 引用计数优化

基本上就是那种大多数 Python 用户永远不会去想的低级工程,除非他们不小心打开了 CPython 内部。

开发者会立即注意到吗?取决于工作负载。

但与早期版本不同,这不再感觉纯粹是理论上的。

3、frozendict

Python 几乎从不添加新的内置数据结构。

所以当它添加时,通常意味着生态系统已经糟糕地重复造了上千次轮子。

这基本上就是 frozendict 的故事。

不可变字典已经在库中存在。人们用元组 hack 它们。人们包装字典。人们使用自定义类。

现在终于有官方版本了。

config = frozendict({"host": "localhost", "port": 5432})

你无法修改它。它是可哈希的。可以安全地用作字典的键。

简单。

老实说,这感觉不太像一个 flashy 的功能,更像是 Python 终于承认了现实。

4、sentinel() 修复了一个 s烦人的模式

这是一个听起来不重要,直到你写了足够多的 Python 才会发现的小功能。

很多代码库这样做

MISSING = object()

为什么?因为 None 可能是一个合法的值。

所以开发者创建匿名的唯一对象作为占位符。

问题是这些对象很丑,没有自文档性,对类型检查不友好,而且通常感觉像是一种变通方法。

现在有了官方 API

NOT_SET = sentinel("NOT_SET")

它更清晰。打印效果更好。与类型系统配合得更好。

这正是 Python 通常擅长的那种语言改进。

不是革命性的。只是减少不必要的怪异。

5、新的性能分析器可能比人们意识到的更重要

Python 的性能分析历来有一个权衡

你要么获得精确但开销巨大的性能分析,要么获得轻量但细节较少的监控。

cProfile 基本上跟踪所有内容。这也意味着它会严重拖慢程序。

现在 Python 3.15 添加了统计采样性能分析器。

这更适合生产环境下的性能分析。

它不是追踪每个调用,而是定期采样执行并估计时间花在哪里。

精确度较低。开销小得多。通常已经足够。

老实说,很多现代性能工具已经这样工作了。Python 在这方面只是 oddly 落后了。

而且,确定性性能分析器不会消失。它们在某些调试场景中仍然很重要。

但对于正常的优化工作,采样性能分析器通常是更现实的选择。

特别是当开发者已经因为性能分析本身会改变运行时行为而犹豫是否要进行性能分析时。

6、错误消息继续变得不那么糟糕

Python 的错误消息在过去几个版本中已经改进了很多。

这听起来很小,直到你将现代 Python 的错误与那些基本上只是对你耸耸肩的旧版本进行比较。

Python 3.15 进一步改进了建议。

一个有趣的例子

my_list.push(1)

Python 现在识别出这很可能是 JavaScript 的 brain damage,并建议

append()

这实际上很有用。

而且说实话,现代编程涉及足够多的语言切换,这些错误是正常的。

属性建议总体上变得更智能了。

不是开创性的。但更好的开发者体验会随着时间的推移而积累。

人们低估了在愚蠢的调试摩擦上浪费了多少精力。

7、在推导式中解包极大地改变了可读性

这个功能可能会把人分成两组

  • "终于"
  • "这让推导式变得不可读了"

两种反应都是合理的。

以前,在推导式中扁平化嵌套可迭代对象看起来像这样:

[a for b in x for a in b]

技术上没问题。但不太令人愉快。

现在

[*a for a in x]

更容易扫描。

字典解包也适用

{**d for d in dicts}

这个功能本身是直接了当的。

更大的问题是,Python 推导式已经有在人们变得太 clever 时变成 competitive programming 谜题的习惯。

所以这可能是那种适度使用很棒,但在认为单行代码是一种个性特征的开发者手中会变得糟糕的功能。

8、垃圾回收器的回滚实际上是一个好迹象

这可能是这个版本在哲学上最有趣的部分。

Python 3.14 引入了增量式垃圾回收器,旨在减少暂停时间。

目标合理。

然后用户开始报告内存使用量增加。有时是 dramatically 增加。

Python 3.15 回滚了这个改动。

而且说实话,这很好。

一个导致内存膨胀的运行时优化,并不仅仅因为实现复杂就值得保留。

在某些生态系统中,有一种倾向是永远捍卫每一个性能实验,因为逆转路线看起来很尴尬。

Python 选择撤回改动,而不是假装用户想象出了问题,这更健康。

旧的 generational 垃圾回收器暂时回归。增量式垃圾回收器可能会在更多工作之后回来。

这很可能是正确的结果。

9、类型系统改进继续扩展 Python 的分裂人格

Python 的类型系统继续进化成它自己的平行宇宙。

有些开发者喜欢这样。有些容忍它。有些仍然把类型提示当作装饰性注释。

Python 3.15 添加了更多 TypedDict 控制,比如

  • closed
  • extra_items

这让字典模式更严格、更具表达力。

还有 TypeForm,它有助于表示已评估的类型表达式。

有用吗?是的。

令人兴奋吗?取决于你在与静态分析工具作斗争上花了多少时间。

现实是,Python 现在同时服务于两个非常不同的受众

  • 想要灵活脚本的人
  • 构建大规模类型系统的人

有时语言很好地处理了这种张力。有时感觉像是两种语言共享语法。

10、Python 3.15 感觉异常实用

这可能就是主要的一点。

很多版本都有普通开发者几乎注意不到的令人印象深刻的内部工作。

Python 3.15 包含了几项直接影响

  • 启动性能
  • 性能分析
  • 运行时速度
  • 错误调试
  • 数据建模
  • 代码可读性

的变化。

不是每个功能都同样重要。有些是专门用途的。有些会在 Reddit 上引发六个月的争论。

但这个版本感觉立足于实际的开发者行为,而不是语言理论。

人们抱怨启动时间。Python 添加了惰性导入。 人们想要不可变字典。Python 添加了一个。 人们报告了 GC 内存问题。Python 撤销了改动。

这种反馈循环可能是这里最令人放心的事情。

我仍然认为一些 Python 应用正变得 absurdly 过度工程化。AI 生态系统尤其似乎决心在打印单个 token 之前导入整个星球。

但至少 CPython 本身似乎越来越关注实际的痛点,而不是假装开发者体验和运行时行为是两个独立的话题。

一旦 3.15 完全发布,我们将看看这些功能在与真实世界项目接触后能存活多少。

一些功能会悄悄地变成常态。一些会立即被滥用。

一些可能会在未来的版本中消失。

这也是非常 Python 的。


原文链接: I Didn't Expect Python 3.15 to Change This Much

汇智网翻译整理,转载请标明出处