2022 年 10 月发布的 Python 3.11,可能是历史上"最该升级"的一个版本。Faster CPython 项目让解释器平均提速 10%–60%,部分基准测试接近翻倍——升级几乎等于白捡性能;同时它还带来了异常组(ExceptionGroup)与 except*、内置 TOML 解析器 tomllib、Self 类型等一批实用新能力。对一个编程语言来说,"性能提升"和"新语法"同时出现在一个版本里,非常罕见。
1. 性能飞跃:自适应专用化解释器
3.11 的核心工作是"自适应专用化(adaptive specialization)":解释器在运行时观察热点代码,把通用字节码替换成针对具体类型优化的专用字节码,配合更快的函数调用与内存分配,整体性能大幅提升。做个最直观的测试——递归斐波那契:
import time
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
start = time.perf_counter()
print(fib(32))
print(f"耗时 {time.perf_counter() - start:.3f}s")
同一台机器上,3.10 通常要 0.5 秒左右,3.11 往往只要 0.3 秒出头,纯计算场景收益明显。官方给出的数据是与 3.10 相比平均快 1.22 倍,部分基准测试快 1.6 倍。对 CPU 密集的脚本、数据处理任务来说,这是实打实的免费升级。
需要说明的是,提速对"纯 Python 循环、函数调用密集"的代码最明显;如果瓶颈在 I/O,或计算已经交给 numpy 这类 C 扩展,提升幅度会小一些。但对脚本、Web 服务和数据预处理,通常都能感受到肉眼可见的差异。
想亲自验证?用 pyenv 并排装 3.10 与 3.11,同一份脚本各跑三遍取中位数再对比。注意把预热代码排除在计时之外,测试期间保持机器空闲,结论才可靠。
2. 异常组:一次抛出多个异常
以前一个函数只能 raise 一个异常,多个错误要么丢弃、要么层层包装。PEP 654 的 ExceptionGroup 可以把多个异常打包抛出,配合 except* 分类捕获:
def validate(data):
errors = []
if "name" not in data:
errors.append(ValueError("缺少 name"))
if "age" not in data:
errors.append(ValueError("缺少 age"))
if "email" not in data:
errors.append(TypeError("缺少 email"))
if errors:
raise ExceptionGroup("表单校验失败", errors)
try:
validate({"name": "小明"})
except* ValueError as eg:
print(f"值错误: {eg.exceptions}")
except* TypeError as eg:
print(f"类型错误: {eg.exceptions}")
异常组特别适合批量场景:并行下载、批量校验、多服务调用——一个任务失败不该吞掉其它任务的结果,现在可以全部收进一个组里统一上报。注意一个坑:except* 不能与裸 except 写在同一个 try 里,否则会直接语法报错。
再比如并发请求多个接口:每个请求的失败都被收集进异常组,调用方一次就能看到全部失败原因,而不是第一个异常就中断——这对重试、告警、统计都非常有用。还要注意 except* 分支之间相互独立:一个分支处理后,未匹配的异常会继续向上传播,直到被其它 except* 或外层 try 捕获。
输出方面,异常组在 traceback 里会按组展示全部成员异常,每个异常都有自己的堆栈;配合 eg.exceptions 遍历,可以逐条写入日志系统,方便事后统计失败率与失败原因分布。
| 语法 | 捕获方式 | 典型场景 |
|---|---|---|
| try / except | 按类型捕获单个异常 | 大多数普通错误处理 |
| except* | 分类捕获异常组中的成员,可多次使用 | 批量任务、并行校验 |
| raise ... from | 链式保留原始异常 | 包装错误上下文 |
3. tomllib:内置 TOML 解析
pyproject.toml 早已是 Python 生态的标准配置文件,但解析 TOML 一直依赖第三方库。3.11 起标准库自带 tomllib(只读解析):
import tomllib
with open("pyproject.toml", "rb") as f:
data = tomllib.load(f)
print(data["project"]["name"])
print(data["project"]["dependencies"])
两个注意点:文件必须以二进制模式("rb")打开;tomllib 只负责读取,写 TOML 仍需 tomli-w 等库。对工具脚本来说,"读配置不再需要任何第三方依赖"本身就是巨大便利。
典型用法是读取 pyproject.toml 的元数据:写发布脚本时提取项目名与版本号、构建工具时读取依赖列表、CLI 工具把参数默认值放在 TOML 配置里——这些以前要引入第三方依赖的活,现在标准库就能搞定。
4. Self 类型:让链式调用类型安全
PEP 673 新增了 Self 类型。以前"返回自身"的类型标注要么写类名字符串、要么用泛型自引用,子类继承时类型还会漂移;Self 让"返回当前类实例"的标注第一次变得准确,链式调用的代码在 IDE 里终于有完整的补全提示:
from typing import Self
class Stack:
def __init__(self):
self.items: list[int] = []
def push(self, item: int) -> Self:
self.items.append(item)
return self
s = Stack().push(1).push(2).push(3)
print(s.items) # [1, 2, 3]
5. 其它 3.11 更新速览
- dataclass(slots=True):数据类直接生成 __slots__,内存占用更小
- typing.NamedTuple 泛型:NamedTuple 终于支持类型参数
- math.exp2 / cbrt:新增指数与立方根等数学函数
- 启动与内存:解释器启动更快,内存占用进一步下降
- 整数转换上限:默认限制十进制字符串转整数的位数,防恶意大数攻击
6. 如何安装 Python 3.11
# pyenv 安装并创建虚拟环境
pyenv install 3.11.11
pyenv virtualenv 3.11.11 myproj
pyenv activate myproj
# 验证并升级 pip
python3.11 --version
python3.11 -m pip install --upgrade pip
7. 值不值得升级?
非常值得,几乎是"无脑升级"的一代:性能白赚、语法兼容性极佳,3.8/3.9/3.10 的代码基本原样运行;异常组和 tomllib 则是实打实的新能力。到 2026 年,3.11 仍在安全维护期(支持到 2027 年 10 月),是生产环境的稳妥之选。升级前只需做两件事:跑一遍完整测试套件;全局搜索代码里是否出现过 except* 与裸 except 混用的隐患。如果你的项目还在 3.7 或更早,3.11 更是"一步到位"的跳板——性能收益能直接抵消升级的人工成本。
唯一的例外是超小型的临时脚本:代码只有几十行、跑一次就扔的话,维持现状也无可厚非。但只要项目会持续维护,升级 3.11 的成本通常半天内就能收回——测试通过之后,性能与生态的双重收益是长期的。
💡 学习建议:升级后第一件事,用自己项目的真实代码跑一遍基准测试,感受性能差异;再写一个会同时产生多个错误的小工具(比如批量检查文件是否存在、是否可读),用 ExceptionGroup 把错误一次性报告出来,体验"一次抛多个异常"的爽快。