服务还在使用 Python 3.13,开发机已经安装 3.14,3.15 又发布了。真正需要判断的是:哪些功能能解决项目里的问题,哪些行为变化会让原来的代码失败,以及如何验证升级没有改变业务结果。

本文以 2026 年 10 月 17 日为时间点,比较三个版本的主要变化。Python 3.13 于 2024 年 10 月 7 日发布,3.14 于 2025 年 10 月 7 日发布,3.15 于 2026 年 10 月 9 日正式发布。后续版本继承前面的功能,表中列出的是各版本新增重点,不能理解成三个互斥的功能集合。

1. 先看版本之间的主要差异

维度 Python 3.13 Python 3.14 Python 3.15
语言和开发体验 新交互终端、类型参数默认值 延迟求值注解、t-string 显式延迟导入、推导式解包
并行能力 实验性自由线程构建 自由线程正式支持;标准库多解释器接口 自由线程构建增加稳定 ABI 支持
标准库代表变化 copy.replace()、dbm.sqlite3 compression.zstd、annotationlib profiling 包及采样分析器 Tachyon
新内置类型 — — frozendict、sentinel
默认编码 受 UTF-8 模式和系统环境影响 受 UTF-8 模式和系统环境影响 默认开启 UTF-8 模式
JIT 实验性引入 持续完善 进一步优化,仍为实验性

表格依据官方 3.13 更新说明、3.14 更新说明、3.15 更新说明。这里讨论 CPython;JIT、自由线程和解释器构建选项需要分别确认。

2. Python 3.13:开发体验改善,自由线程开始进入实际测试

日常最容易感受到的是交互终端:多行编辑、历史记录、彩色错误信息和粘贴模式更好用。类型提示新增类型参数默认值、TypeIs 和 TypedDict 的 ReadOnly 等能力,主要帮助静态检查和库接口设计,不会自动变成业务输入校验。

copy.replace() 适合“保留原对象,生成部分字段改变的新对象”。下面保存为 demo_replace.py,使用 Python 3.13 以上执行:

from copy import replace
from dataclasses import dataclass


@dataclass(frozen=True)
class RetryPolicy:
    """保存重试策略,让调用方明确创建修改后的新配置。"""

    attempts: int
    timeout_seconds: float


original = RetryPolicy(attempts=2, timeout_seconds=3.0)
updated = replace(original, attempts=4)
assert original.attempts == 2
assert updated == RetryPolicy(attempts=4, timeout_seconds=3.0)
print("原配置未改变,新配置已生成")

它支持 dataclass、namedtuple 和实现相应协议的对象,不能当作任意对象的通用深拷贝器。

3.13 同时引入实验性无 GIL 构建和实验性 JIT。前者让适合的线程工作负载利用多个 CPU 核心;后者尝试优化热点代码。它们不是同一件事,也不能据此承诺普通 Web 接口升级后会获得固定比例的性能提升。

老项目还要检查被删除的模块,例如 cgi、telnetlib、audioop。直接调用或间接依赖它们的代码,可能在导入时失败。详细清单见 3.13 移植说明。

3. Python 3.14:注解、模板和并行计算更值得关注

3.1 注解延迟求值,前向引用更自然

以下示例保存为 demo_annotations.py,需要 Python 3.14 以上。示例不启用 from __future__ import annotations,以便直接展示新的默认行为:

from annotationlib import get_annotations


def make_customer() -> Customer:
    """返回后面定义的模型,用于演示前向引用。"""
    return Customer()


class Customer:
    """表示示例客户。"""


assert isinstance(make_customer(), Customer)
assert get_annotations(make_customer)["return"] is Customer
print("前向引用与注解读取验证通过")

前向引用不必为了定义顺序而全部写成字符串。但如果某个类型始终没有定义,实际求值注解时仍可能出现错误。使用依赖注入、数据模型和运行时注解反射的项目,应测试框架读取注解的路径,而不只是检查模块能否导入。官方说明

3.2 t-string 保留插值结构

f-string 直接产生字符串;t-string 产生模板对象,处理器可以分别取得静态文本和插入值。以下保存为 demo_template.py,需要 Python 3.14 以上:

from string.templatelib import Template


customer_name = "王明"
template = t"客户:{customer_name}"
assert isinstance(template, Template)
assert template.strings == ("客户:", "")
assert template.values == (customer_name,)
print("模板文本与插入值保持分离")

这适合模板处理库设计。模板对象本身不会自动完成 HTML 转义,也不会把 SQL 变成参数化查询;数据库操作仍应使用驱动的绑定参数接口。模板对象文档

3.3 多解释器提供另一种多核计算方式

3.14 的 InterpreterPoolExecutor 让工作线程使用独立解释器,适合评估纯 Python 计算任务。下面保存为 demo_interpreters.py,在支持该功能的平台上使用 Python 3.14 以上执行:

from concurrent.futures import InterpreterPoolExecutor


def sum_squares(limit: int) -> int:
    """计算平方和,作为不依赖外部服务的独立计算任务。"""
    return sum(number * number for number in range(limit))


if __name__ == "__main__":
    limits = [1000, 2000, 3000]
    expected = [sum_squares(limit) for limit in limits]
    with InterpreterPoolExecutor(max_workers=2) as executor:
        results = list(executor.map(sum_squares, limits))
    assert results == expected
    print("并行计算结果与串行计算一致")

独立解释器有独立状态,传输数据、初始化和第三方扩展适配都有成本。这个小例子只检查结果一致性,任务太小,不适合作为加速基准。真实评估要使用实际输入,比较耗时、峰值内存和失败处理。执行器文档

此外,3.14 新增 Zstandard 标准库支持、增强 asyncio 任务排查能力,并正式支持自由线程 Python。正式支持不等于普通构建默认取消 GIL。

4. Python 3.15:延迟导入、不可变字典与默认 UTF-8

4.1 延迟导入主要改变启动阶段

一个包含多个子命令的工具,可能仅执行帮助命令就导入了报表、数据库和绘图依赖。3.15 可以显式声明延迟导入,在第一次使用名称时才真正加载模块。

下面保存为 demo_lazy.py,仅能用 Python 3.15 以上执行:

lazy import json

print("命令入口已启动")
payload = json.loads('{"task_count": 3}')
assert payload["task_count"] == 3
print("读取结果:", payload["task_count"])

这改变了导入副作用和异常的发生时机。插件注册、日志初始化等若依赖导入时执行,不能机械地批量改成延迟导入。应先定位启动开销,再选择没有这类依赖的模块。旧解释器无法解析 lazy import,在代码后面加版本判断也救不了解析错误。语法限制与兼容迁移方式见 PEP 810。

4.2 frozendict 和 sentinel 各解决一个明确问题

frozendict 用于创建后不允许增删键或替换值的映射;它不是 dict 的子类,严格判断 isinstance(value, dict) 的边界需要重新考虑。它也不会递归冻结值中的列表等对象。

sentinel 用于表示独立的“缺失”状态。例如更新客户电话的接口需要区分:不传字段表示不修改,传 None 表示清空。新类型提供专门的标记能力,减少到处自行创建缺失标记的做法。

3.15 还增加推导式解包、新的性能分析包和 Tachyon 采样分析器,并推进自由线程构建的稳定 ABI。ABI 的进展降低扩展维护负担,但不能替代具体扩展库对线程安全和新版本的支持声明。详见 3.15 新功能说明。

4.3 默认 UTF-8 是 Windows 老脚本的重要检查点

3.15 默认开启 UTF-8 模式。原来依赖系统区域设置读取 GBK 文本的脚本,升级后可能出现解码错误。这不会自动把旧文件转换为 UTF-8。

下面的跨版本示例保存为 demo_encoding.py,使用 Python 3.13 以上执行:

from pathlib import Path
from tempfile import TemporaryDirectory


with TemporaryDirectory() as directory:
    path = Path(directory) / "legacy.txt"
    path.write_bytes("订单已完成".encode("gbk"))
    assert path.read_text(encoding="gbk") == "订单已完成"
    try:
        path.read_text(encoding="utf-8")
    except UnicodeDecodeError:
        print("已复现编码不匹配,读取时应指定实际编码")
    else:
        raise AssertionError("测试数据未触发预期的解码错误")

修复方向是明确文件或协议约定,而不是统一加 errors="ignore" 丢弃数据。调用外部命令时也不能因为 Python 改了默认编码,就假设对方输出一定是 UTF-8。默认模式、兼容开关和迁移建议见 PEP 686。

5. 升级前怎样检查,升级后怎样验收

先记录实际运行解释器。以下 PowerShell 命令使用当前激活环境的 python;应用服务、计划任务和 IDE 的解释器路径还要分别核对:

python -VV
python -c "import sys; print(sys.executable); print(sys.flags.utf8_mode)"
python -m pip check
python -W default -m unittest discover -s tests -v

pip check 只能检查已安装依赖的声明是否互相满足,不能证明它们支持目标 Python。tests 是项目测试目录,若项目使用 pytest 或其他测试入口,应替换最后一条命令。-W default 用于展示弃用等警告,不能替代结果断言。

实际迁移按下面顺序进行:

  1. 保留当前解释器和依赖锁文件,为目标版本建立独立环境;不要原地覆盖生产虚拟环境。
  2. 检查核心依赖的 Python 版本声明,以及目标操作系统、CPU 架构、普通或自由线程构建对应的安装包。
  3. 先不引入新语法,验证原有代码在新解释器中的行为;通过后再分别采用新功能。
  4. 回归导入、中文文件读写、运行时注解、数据库事务、并发取消和任务退出。真实项目按所用功能增减。
  5. 在相同机器和输入下比较启动时间、吞吐、延迟及内存。不要只用一次微基准决定上线。
  6. 灰度切换服务启动配置,确认实际进程路径;出现回归时恢复原启动配置和原依赖环境。

另一个常见问题是 Linux 多进程启动方式。3.14 在支持条件下调整了默认方式,代码如果依赖父进程隐式继承的资源,应明确验证初始化和对象传输,不要依赖旧默认行为。使用标准库多进程的项目可参考 3.14 兼容性说明。

6. 本文验证范围与选型建议

本文示例按适用版本拆成独立文件,避免旧解释器解析新语法。发布前使用本机 Python 3.14.6 执行副本更新、延迟注解、t-string、多解释器计算和编码复现五个示例;3.15 的延迟导入示例依据官方文档核对,本机没有可用的 3.15 解释器,未执行。未进行跨版本性能基准,也未验证具体项目依赖。

已有 3.13 项目不需要仅因版本号变化立刻迁移;先确认升级目的。评估 3.14 时,重点看注解读取和并发路径;评估 3.15 时,再增加默认编码、延迟导入副作用和新版本依赖安装的检查。

选择版本时可以把“解释器升级”和“代码采用新特性”拆成两次变更。前一次验证兼容性,后一次验证功能收益;出了问题,更容易定位到具体变化,也更容易回退。