本文目录导读:

这是一个很好的技术问题,简单直接的回答是:对于纯 Python 代码和标准 C API,ABI 并不是 100% 长期稳定,而是通过 PEP 384(稳定 ABI)提供了一个有限的稳定子集;但对于 Python 3.2+ 的稳定 ABI,它确实是保持向后兼容的。
下面详细解释一下这个“稳定”的含义和现状:
核心概念:ABI 不稳定 vs. 稳定 ABI
- 完整的 Python C API(不稳定): Python 解释器内部的数据结构(如
PyObject内部布局、PyTypeObject的字段等)在不同的次版本(minor version,如 3.9 -> 3.10)之间可能会改变,直接使用完整 C API 编译的扩展模块,通常只能用于编译它时所用的那个精确的 Python 版本(为 Python 3.9.10 编译的,可能无法在 3.9.11 甚至 3.10 上运行,更不用说跨主版本了)。 - 稳定 ABI(PEP 384): 从 Python 3.2 开始,定义了一个稳定 ABI(Stable ABI),它是一组 API 函数的子集,这些函数被官方承诺在未来版本中保持二进制兼容,只要扩展模块只使用稳定 ABI 中的函数和宏(并且这些函数的行为不变),编译出来的
.pyd或者.so文件就可以在未来的 Python 版本(包括 3.x, 4.x...)上直接运行,无需重新编译。
稳定 ABI 的现状(截至 Python 3.12/3.13)
- 确实是稳定的: Python 核心开发团队已经承诺并维护了稳定 ABI 的向后兼容性,自 Python 3.2 引入以来,稳定 ABI 的定义(即哪些函数是可用的)一直在扩展,但没有破坏过已有的函数,这意味着如果一个模块只使用稳定 ABI 并在 Python 3.8 上编译,它理论上应该(并且实际上经常)能在 Python 3.13 上运行。
- 不是所有模块都使用它: 大多数用 C 写的 Python 扩展(NumPy、PyTorch、Pandas、OpenCV 等)并没有完全限制自己只使用稳定 ABI,它们为了追求极致性能或访问底层数据结构,经常使用非稳定的、完整 C API,这些库仍然需要为每个 Python 主版本(甚至次版本)重新编译分发。
- Python 3.12+ 的变化: 近期版本(特别是 Python 3.12, 3.13)对解释器内部结构进行了重大重构(移除帧对象、重写垃圾回收器),这导致非稳定 API 的 ABI 再次断裂,但稳定 ABI 本身毫发无损,因为这部分重构只影响了内部数据结构。
如何判断和利用稳定 ABI
- 编译器标志: 在编译扩展模块时,如果你使用
Py_LIMITED_API宏(并设置为一个具体版本,如0x030C0000代表 Python 3.12),编译器会自动限制你可以使用的 API,只允许使用稳定 ABI 中的函数,生成的二进制文件会是一个“受限的”扩展。 - 文件命名: 使用稳定 ABI 编译的扩展,其文件名中通常没有完整的 Python 版本号,之前的可能叫做
mymodule.cpython-312-x86_64-linux-gnu.so,使用稳定 ABI 后可能变成mymodule.cpython-312-x86_64-linux-gnu.so(实际上标准规则是mymodule.abi3.so或mymodule.cp312-abi3-...,后缀包含abi3),这方便了分发,因为一个.so文件可以覆盖多个 Python 小版本。
实际建议
- 对于纯 Python 代码: 100% 稳定,跨版本(Python 2/3 除外,那是不兼容的)。
- 对于 C 扩展的标准情况: 不要假设完整 ABI 稳定,第三方库(如 numpy、scipy)通常为每个 Python 次要版本(3.10, 3.11, 3.12...)提供单独的 wheel。
- 对于 C 扩展的“稳定 ABI”方案: 如果你正在编写一个 C 扩展,并且可以接受微小的性能代价(稳定 ABI 的函数通常是间接调用的,略慢),那么优先使用
Py_LIMITED_API编译,这样你的模块将获得跨次版本和主版本的兼容性(至少从 Python 3.2 到未来),这是 Python 官方推荐的分发纯 C 扩展模块的方式。 - 对于 Cython/其他工具: Cython 等工具默认会使用完整 API,但也可以配置为使用稳定 ABI。
| 特性 | 完整 C API | 稳定 ABI (PEP 384) |
|---|---|---|
| 稳定性 | 不稳定(随版本变化) | 长期稳定(官方承诺) |
| 性能 | 较高 (直接访问) | 略低 (通过函数指针间接访问) |
| 兼容范围 | 仅编译本版本 | 跨 Python 3.x 次版本,甚至主版本 |
| 使用示例 | NumPy, Pandas, PyTorch | Python 标准库中的一些模块,ctypes |
最终结论: 如果你是指扩展模块能不经重新编译就在不同 Python 版本间运行,那么对于绝大多数流行的 C 答案是否定的(因为没使用稳定 ABI),但 Python 官方确实提供了一个稳定的 ABI 规范,只要你遵循它,你的模块就能获得跨版本的二进制兼容性。