Python 3.14 迁移指南#
本章提供从 Python 3.13(或更早版本)迁移到 Python 3.14 的实用指南,包括废弃 API 对照、行为变更注意事项、C 扩展迁移步骤和常见问题解决方案。
1. Python 3.13→3.14 迁移 Checklist#
升级前准备#
[ ] 阅读本指南的行为变更部分(第 3 节)
[ ] 在测试环境中使用 Python 3.14 运行测试套件
[ ] 检查依赖库是否支持 Python 3.14(特别是 C 扩展)
[ ] 如需使用自由线程,检查 C 扩展的 FT 兼容性
[ ] 关注增量 GC 回退说明(3.14.5+)
代码变更#
[ ] 移除
from __future__ import annotations(可选,软弃用)[ ] 修复
finally块中的return/break/continue(SyntaxWarning)[ ] 检查
NotImplemented的布尔上下文使用(TypeError)[ ] 废弃 API 替换(见下表)
[ ]
map()使用strict=True防止静默截断[ ] 测试类型注解在 3.14 下的行为(延迟注解)
测试验证#
[ ] 运行完整测试套件,确保所有测试通过
[ ] 测试自由线程模式(
python3.14t或PYTHON_GIL=0)[ ] 测试 JIT 模式(
PYTHON_JIT=1)[ ] 性能基准测试(单线程和多线程场景)
2. 废弃 API 对照表#
3.13 API |
3.14 状态 |
替代方案/修复建议 |
|---|---|---|
|
软弃用 |
删除即可,PEP 649 延迟注解默认启用 |
|
弃用 |
使用 asyncio 新 API |
|
弃用 |
使用内置 |
|
软弃用 |
使用 |
|
软弃用 |
使用 |
|
弃用 |
使用 |
|
移除 |
不再需要,asyncio 重构了子进程管理 |
|
移除 |
使用 |
|
移除 |
使用 |
|
移除 |
使用标准构造参数 |
|
移除 |
使用 C99 |
|
移除 |
使用 C99 |
|
移除 |
使用 |
3. 重要行为变更#
multiprocessing 默认启动方法变更#
Unix(非 macOS):默认启动方法从 fork 改为 forkserver。
import multiprocessing
print(multiprocessing.get_start_method())
# Linux 3.13: 'fork'
# Linux 3.14: 'forkserver'(更安全,避免 fork-after-multithreading 问题)
影响:如果依赖 fork 的特定行为(如继承所有内存状态),需要显式设置:
multiprocessing.set_start_method('fork', force=True)
functools.partial 变为方法描述符#
functools.partial 对象现在正确支持方法绑定:
from functools import partial
class MyClass:
def method(self, x, y):
return x + y
add5 = partial(method, 5)
obj = MyClass()
obj.add5(3) # 3.13: TypeError(partial 不绑定 self)
# 3.14: 8(正确绑定 self,等价于 obj.method(5, 3))
types.UnionType 与 typing.Union 等价#
int | str 返回的 types.UnionType 与 typing.Union[int, str] 在所有场景下完全等价:
import typing
assert type(int | str) is typing.UnionType # 3.14 保证
pickle 错误类型变化#
某些 pickle 错误现在抛出不同的异常类型(更具体的异常类型而非通用 PicklingError)。确保错误处理不会因为异常类型变化而失效。
int() 不再委托 trunc#
int() 不再调用 __trunc__() 方法。自定义数值类型应实现 __int__() 而非 __trunc__():
class MyNumber:
# ❌ 旧方式
def __trunc__(self):
return 42
# ✅ 新方式
def __int__(self):
return 42
NotImplemented 布尔上下文 TypeError#
在布尔上下文中使用 NotImplemented 现在抛出 TypeError:
# ❌ 3.14 中会抛 TypeError
if NotImplemented:
pass
# 富比较方法中也要注意
class MyClass:
def __eq__(self, other):
result = compare(self, other)
if not result: # ❌ result 可能是 NotImplemented
return False
return result
# ✅ 正确写法
def __eq__(self, other):
result = compare(self, other)
if result is NotImplemented:
return NotImplemented
return result
⚠️ 增量 GC 回退(3.14.5)#
Python 3.14.0 引入了增量 GC(Incremental GC),旨在减少 GC 暂停时间。但在 3.14.1-3.14.4 中收到生产环境内存压力报告后,3.14.5 回退到 3.13 式分代 GC:
3.14.0-3.14.4:增量 GC(可能有内存使用增加)
3.14.5+:分代 GC(与 3.13 行为一致)
如果在 3.14.0-3.14.4 中遇到内存问题,升级到 3.14.5+ 即可。
4. C 扩展迁移指南#
第一步:确保编译通过#
// 替换已移除的数学宏
// ❌ #include <Python.h> 中的 Py_IS_NAN
// ✅
#include <math.h>
#define Py_IS_NAN(x) isnan(x) // 或直接使用 isnan()
// 替换 PySequence_Fast_GET_ITEM
// ❌ item = PySequence_Fast_GET_ITEM(seq, i);
// ✅
PyObject *fast = PySequence_Fast(seq, "");
if (!fast) goto error;
PyObject *item = PyList_GET_ITEM(fast, i);
// ... 使用 ...
Py_DECREF(fast);
// 不直接访问 ob_refcnt
// ❌ obj->ob_refcnt++;
// ✅ Py_INCREF(obj);
// ❌ obj->ob_type = &MyType;
// ✅ Py_SET_TYPE(obj, &MyType);
第二步:添加 GIL 依赖声明#
// 模块定义中添加 Py_mod_gil 槽位
static struct PyModuleDef_Slot mymodule_slots[] = {
{Py_mod_gil, Py_MOD_GIL}, // 声明依赖 GIL(安全默认)
{0, NULL}
};
static struct PyModuleDef mymodule = {
PyModuleDef_HEAD_INIT,
.m_name = "mymodule",
.m_size = -1,
.m_methods = mymethods,
.m_slots = mymodule_slots,
};
这确保你的扩展在 python3.14t(自由线程构建)中仍然能正常工作(自动获取 GIL)。
第三步(可选):适配自由线程#
// 对于对象的可变字段访问,使用关键区段
static PyObject*
myobj_get_data(MyObject *self, void *closure)
{
Py_BEGIN_CRITICAL_SECTION(self);
PyObject *data = Py_XNewRef(self->data);
Py_END_CRITICAL_SECTION();
return data;
}
static int
myobj_set_data(MyObject *self, PyObject *value, void *closure)
{
Py_BEGIN_CRITICAL_SECTION(self);
Py_XSETREF(self->data, Py_XNewRef(value));
Py_END_CRITICAL_SECTION();
return 0;
}
完成适配后,将 Py_MOD_GIL 改为 Py_MOD_FREE_THREADED。
第四步:验证#
# 在标准构建下测试
python3.14 -m pytest tests/
# 在自由线程构建下测试
python3.14t -m pytest tests/
# 启用 JIT 测试
PYTHON_JIT=1 python3.14 -m pytest tests/
5. 字节码变更对工具的影响#
Python 3.14 的字节码有以下变更:
新增 t-strings 相关指令
新增/调整特化指令
某些操作码编号变化
影响范围:
✅ 普通应用:无影响
⚠️ 字节码操作库(如
dis、bytecode、xdis):需要更新⚠️ 代码覆盖工具:可能需要适配新的行号映射
⚠️ AOT 编译器(Nuitka、Cython):需要适配新字节码