
为什么 LangChain、PyTorch 都盯着 3.10?
如果你最近折腾过 AI 相关的东西——LangChain、PyMuPDF、甚至 PyTorch 的新版本——大概率见过这行字:
Requires Python >= 3.10
是不是一脸懵?
我的 3.8 不香了吗?我的 3.9 怎么就不配了?
答案很简单:Python 从 3.10 开始,才真正变"现代"了。
3.10 之前的 Python,虽然能跑,但类型系统弱、错误提示烂、语法啰嗦。对于动辄几万行代码的 AI 框架来说,这些都是实打实的工程痛点。
从 3.10 到 3.14,Python 用了四年时间,把自己从一辆二手桑塔纳,改装成了一辆特斯拉。
引擎换了(Faster CPython、JIT、Free-threaded),底盘换了(类型系统大改),内饰也换了(REPL、f-string、模板字符串)。
今天咱们就一个版本一个版本地掰开聊,看看这条升级链上,每一环到底干了啥。
3.10:语法与类型友好元年
发布日期:2021 年 10 月 4 日
match/case——Python 终于有 switch 了
写 JavaScript 的学友都知道 switch。写 TypeScript 的更知道 discriminated union 那套玩法——根据 type 字段做模式匹配。
Python 在 3.10 之前?if/elif/elif/elif/else,写到你手指抽筋。
3.10 引入了 match/case,这不是简单的 switch/case 翻版——它是结构化模式匹配。支持解构、守卫条件、类型匹配,比 JS 的 switch 强了不止一个档次。
match command:case {"action": "buy", "item": str(name), "quantity": int(qty)}: process_purchase(name, qty)case {"action": "refund", "item": str(name)}: process_refund(name)case _:raise ValueError("未知指令")
看到没?直接解构字典,连类型检查都带上了。写过 Rust 或 Elixir 的学友会觉得特别亲切。
X | Y 联合类型——跟 TypeScript 一个味
以前写联合类型:Union[int, str],得从 typing 模块导入 Union。
3.10 开始,直接写 int | str。
# Before 3.10from typing import Uniondef greet(name: Union[str, None]) -> str: ...# After 3.10def greet(name: str | None) -> str: ...
TypeScript 开发者看到这个,应该会心一笑:这不就是我们的 string | null 吗?
错误提示——终于不说"人话"了
3.10 之前 Python 的错误提示,堪比"你有错,但我不告诉你哪错了"。
到了 3.10,解释器终于学会精确报错了。少个括号?它会告诉你哪里少了。拼错了变量名?它还会猜你是不是想写那个。
这对新手来说是救命的改动。
对老司机来说嘛——你别说,有时候凌晨两点 debug,能少看 10 秒报错就是赚了。
3.10 的意义
Python 在这个版本第一次让人觉得:写起来舒服了。
不是"能跑就行"的那种能用,而是"写类型不膈应、匹配模式有手感".这是 Python 正式向现代语言看齐的起点。
3.11:性能 + 异常处理升级
发布日期:2022 年 10 月 24 日
Faster CPython——快了 25%
这是官方数据,不是我编的:
CPython 3.11 is an average of 25% faster than CPython 3.10 as measured with the pyperformance benchmark suite, when compiled with GCC on Ubuntu Linux. Depending on your workload, the overall speedup could be 10-60%.
25% 是什么概念?你的训练脚本原来跑 10 分钟,升级 3.11 可能变成 7.5 分钟。什么都不用改,换个解释器就行。
不过话说回来,Python 还是比 C 慢几十上百倍——别指望它追上 Rust。但在 Python 自己的赛道里,25% 已经非常恐怖了。这是 Faster CPython 项目(由微软赞助、Guido 亲自参与)交出的第一份答卷。
ExceptionGroup + except*——一次接住一把异常
以前一个 try/except 只能捕获一个异常。但 async 场景下,多个协程可能同时炸——怎么办?
3.11 引入了 ExceptionGroup,以及配套的 except* 语法:
try:async with asyncio.TaskGroup() as tg: tg.create_task(might_fail_1()) tg.create_task(might_fail_2())except* ValueError as eg:for e in eg.exceptions:print(f"捕获到 ValueError: {e}")except* TypeError as eg:for e in eg.exceptions:print(f"捕获到 TypeError: {e}")
如果你写前端,可以类比 Promise.allSettled() ——区别是 Python 现在在语言层面支持一次处理多个异常了,不用自己拿数组接。
精准错误定位——traceback 指哪打哪
3.11 做了一个小但美的事:traceback 精确到表达式级别。
以前报错,告诉你"第 42 行有问题"。
现在报错,用波浪线指出这一行里哪个表达式出了问题:
Traceback (most recent call last): File "example.py", line 1 x['a']['b']['c']['d'] = 1 ~~~~~~~~~~~^^^^^TypeError: 'NoneType' object is not subscriptable
这个改动在调试长链式调用(.a.b.c.d)时非常救命。
原生 tomllib——读 TOML 不求人
TOML(Tom's Obvious, Minimal Language)是现代 Python 项目的标准配置格式。现在 pyproject.toml 已经基本取代了 setup.py。
但在 3.11 之前,标准库不支持读 TOML。你得装第三方库 tomli。
3.11 把它收编了,改名 tomllib,正式进了标准库。
import tomllibwith open("pyproject.toml", "rb") as f: config = tomllib.load(f)
注意:tomllib 只支持读,不支持写。写 TOML 依然需要第三方库(比如 tomli-w)。
3.11 的意义
异常处理现代化了,性能也开始认真卷了。Python 不再只是"好写"的语言,它开始"跑得也不算太丢人"了。
3.12:类型语法革命 + f-string 解放
发布日期:2023 年 10 月 2 日
PEP 695——泛型终于能好好写了
3.12 之前写泛型:
from typing import TypeVar, GenericT = TypeVar("T")class Stack(Generic[T]):def push(self, item: T) -> None: ...
三行代码才能声明一个类型变量,而且那个 TypeVar("T") 重复写名字,强迫症看了想打人。
3.12 直接给你简化成:
class Stack[T]:def push(self, item: T) -> None: ...
TypeScript 开发者应该很眼熟吧?class Stack<T> 嘛。Python 终于追上来了。
同样的,类型别名也简化了:
# Beforefrom typing import TypeAliasVector: TypeAlias = list[float]# After 3.12type Vector = list[float]
f-string 大解放
3.12 之前的 f-string 有一堆诡异限制:
3.12把 f-string 的解析器重写了,上面这些限制全部取消。
# 3.12 之前这么写会报错f"{'hello'}"# 内外都用单引号?不行!# 3.12 开始可以了f"{'hello'}"# 没问题f"""result = { some_function( arg1, # 注释也能写了 arg2 )}"""
这对写复杂的 SQL 拼接、日志模板、前端模板渲染的学友来说,简直解放双手。
@override 装饰器
引入了 typing.override 装饰器。你在子类重写父类方法时加上它,如果父类根本没那个方法,类型检查器会报错。
这和 TypeScript/Java 的 @override / override 关键字一个意思——防你手滑重写了个不存在的方法。
distutils 彻底删除
distutils 这个古老的打包模块,终于在 3.12 被正式移除了。
如果你还有代码 import distutils,赶紧改用 setuptools 或者直接投奔 pyproject.toml + build 工具链。
这对大部分现代项目影响不大,但有些老项目的 setup.py 可能会炸。迁移文档建议看 setuptools 官方迁移指南。
3.12 的意义
写类型和字符串拼接的体验大幅提升。如果你之前嫌 Python 的类型注解啰嗦,3.12 足以改变你的看法。
3.13:无 GIL 实验 + 全新 REPL
发布日期:2024 年 10 月 7 日
Free-threaded CPython——GIL 动摇了
GIL(Global Interpreter Lock,全局解释器锁),Python 最大的历史包袱。它让 Python 的多线程基本是"假的"——同一时刻只有一个线程在跑 Python 字节码。
3.13 首次提供了实验性的 Free-threaded 构建。你可以编译或下载一个不带 GIL 的 CPython,让多个线程真正并行执行 Python 代码。
⚠️ 但是:这是实验性的。官方明确说了,不适合生产环境。很多 C 扩展库还没适配无 GIL 模式,贸然使用可能出各种奇怪的 bug。
不过方向已经定了——GIL 的终结只是时间问题。
全新交互式 REPL
Python 的老 REPL(就是你在终端敲 python 出来的那个 >>> 界面)终于被翻新了。
新 REPL 基于 PyPy 的 _pyrepl,支持:
虽然不能跟 IPython / Jupyter 比功能量,但作为开箱即用的体验,质的飞跃。
实验性 JIT 编译器
3.13 还悄悄加了一个实验性的 JIT(Just-In-Time)编译器。它基于一种叫"copy-and-patch"的技术,是 Faster CPython 项目的延续。
目前这个 JIT 不会让大部分代码变快,但它是未来性能优化的基础设施。官方的态度很明确:先把架子搭好,后面再调性能。
19 个老模块大清理
2019 年提出的"dead batteries"清理计划,在 3.13 正式执行:19 个标准库模块被移除。
包括 aifc、audioop、cgi、cgitb、chunk、imghdr、mailcap、msilib、nis、nntplib、ossaudiodev、pipes、sndhdr、spwd、sunau、telnetlib、uu、xdrlib 以及 crypt。
这些模块大多是上世纪 90 年代的遗产,比如 telnetlib(谁还在用 telnet?),nis(谁还在用 NIS?)。
如果你的项目还在用它们,PyPI 上有对应的重新分发包(如 standard-cgi、audioop-lts 等),可以作为临时替代方案。
3.13 的意义
Python 开始认真解决两个根本性问题:多核并行和日常开发体验。Free-threaded 是未来的方向,新 REPL 则是活在当下的改进。
3.14:模板字符串 + 延迟注解 + 多解释器
发布日期:2025 年 10 月 7 日
t-string——Python 的 Tagged Template
JavaScript 有 tagged template literals:
html`<h1>${title}</h1>`
Python 3.14 引入了类似的东西——模板字符串(template string),用 t"..." 前缀标记:
name = "World"template = t"Hello, {name}!"# template 不是 str,而是 Template 对象# 你可以在处理函数中拿到原始片段和插值部分
跟 f-string 不同,t-string 不会立即拼接成字符串。它返回一个 Template 对象,你可以拿到原始字面量片段和插值表达式的值,然后自己决定怎么处理。
这对安全拼接 SQL、HTML、日志等场景非常有用——从语言层面防止注入。
注解延迟求值
Python 的类型注解长期以来有一个尴尬:它在定义时就求值。
这导致两个问题:
- 前向引用:类 A 引用了类 B,但 B 还没定义——就得写成字符串
"B"
3.14 让注解变成延迟求值——只有在你真正需要它(比如通过 typing.get_type_hints() 或 annotationlib.get_annotations() 获取)的时候,才会计算。
结果就是:前向引用问题基本解决了,模块加载也变快了。
concurrent.interpreters——标准库原生多解释器
3.12 在 C API 层面引入了 per-interpreter GIL,3.14 把这个能力暴露给了 Python 层。
新增的 concurrent.interpreters 模块允许你创建多个子解释器,每个子解释器有自己独立的 GIL,可以真正并行执行。
import concurrent.interpreters as interpretersinterp = interpreters.create()interp.exec("print('Hello from sub-interpreter!')")
如果说 Free-threaded 是"拆掉 GIL"这条路,那多解释器就是"每人一把锁,互不干扰"这条路。两条路最终可能殊途同归,但策略不同。
其他改进
- 增量 GC(Incremental Garbage Collection):垃圾回收不再一次性暂停全部,减少延迟抖动
- REPL 语法高亮:交互式解释器的颜色支持进一步增强
- Free-threaded 模式进一步成熟:3.14 中 free-threaded 构建得到显著加强,生态兼容性提升(目前仍为实验性支持)
3.15 会有什么?
截至本文写作时(2026 年 4 月),Python 3.15 仍在 feature 阶段(主开发分支),计划于 2026 年 10 月发布。Release Manager 是 Hugo van Kemenade。
具体会包含哪些特性,目前仍在 PEP 讨论和 alpha 迭代中。已确认的部分特性包括 PEP 810(显式惰性导入)、PEP 814(frozendict)、PEP 799(新 profiling 包)等,更多特性仍在 alpha 阶段可能调整。
可以持续关注的方向:
- Free-threaded 模式的成熟度和生态适配进展
各版本 EOL 时间表
注意:3.10 的 EOL 是 2026 年 10 月——也就是说,如果你现在读到这篇文章,3.10 可能已经接近或进入 EOL 了。如果你的生产环境还在跑 3.10,是时候认真规划升级了。
3.9 已经于 2025 年 10 月 31 日终止支持。更老的 3.8 在 2024 年 10 月 7 日就已经 EOL。
尾声:一条蛇的自我革命
回头看这五个版本,你会发现一条清晰的主线:
Python 正在把自己从"好用的脚本语言"升级成"好用的工程语言"。
类型系统在向 TypeScript 学习。
并行能力在向 Go/Erlang 看齐。
模板字符串在向 JavaScript 致敬。
性能优化在向 JIT 语言(Java/C#)的方向努力。
它没有推翻自己的设计哲学("There should be one-- and preferably only one --obvious way to do it"),而是在这个哲学内部做最大化的现代化改造。
但话说回来,语言的升级永远比项目的升级快。你的语言是 3.14 了,你的 requirements.txt 可能还在用三年前的依赖版本。你的类型注解写得再漂亮,CI 里可能根本没跑 mypy。
升级语言很容易,升级工程实践才是真正的硬仗。
我是幽皇,今天就到这里。我们下篇见。