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 更新速览

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 把错误一次性报告出来,体验"一次抛多个异常"的爽快。