Python需要C扩展是因为CPython解释器用C编写,C扩展是突破GIL、调用系统资源、复用高性能库及极致优化的唯一路径;它通过Python.h API实现类型转换与对象管理,需严格处理引用计数和参数解析。

为什么 Python 需要 C 扩展
Python 解释器本身是用 C 写的,CPython 的运行时、内存管理、对象模型全靠 C 层支撑。当你需要突破 Python 自身限制——比如绕过 GIL 做真正并行计算、直接操作硬件/系统调用、复用已有高性能 C/C++ 库(如 OpenCV、NumPy 底层)、或对关键热路径做极致优化——C 扩展不是“可选”,而是唯一可行路径。
PyLong_FromLong 这类 API 是怎么被调用的
Python 提供了一套稳定的 C API(头文件 Python.h),所有内置类型和核心行为都通过它暴露。写扩展时你不是在“嵌入 Python”,而是在“作为 C 模块被 Python 加载”:定义 PyMethodDef 表声明函数,实现 PyInit_mymodule 初始化函数,再用 PyLong_FromLong、PyList_GetItem 等把 Python 对象转成 C 值处理,最后再封装回 Python 对象返回。
常见错误现象:SystemError: bad argument to internal function 多因没检查输入对象类型就强转;Segmentation fault 常因忘了调用 Py_INCREF/Py_DECREF 管理引用计数。
- 必须用
PyArg_ParseTuple或PyArg_Parse安全解析参数,别直接访问PyObject*成员 - 所有从 Python API 返回的对象,若要长期持有,必须显式
Py_INCREF - 模块初始化失败时,必须返回
NULL,不能返回空指针或忽略错误
比 ctypes/cffi 更底层,也更难逃坑
ctypes 和 cffi 适合调用已有 C 动态库,但它们无法绕过 GIL、不能自定义类型、也不能深度干预 Python 对象生命周期。C 扩展能注册自定义 PyTypeObject,实现带 C 成员的类,支持 __getbuffer__ 协议(零拷贝内存视图),还能在不释放 GIL 的前提下执行纯计算(需手动调用 Py_BEGIN_ALLOW_THREADS)。
立即学习“Python免费学习笔记(深入)”;
性能差异真实存在:一个纯循环累加整数,C 扩展比等效 Python 代码快 50–100 倍;但若频繁在 C/Python 边界来回传小对象,反而可能更慢——因为序列化/反序列化开销压倒了计算收益。
现代替代方案没取代 C 扩展,只是分流了需求
Cython 本质是生成 C 扩展的 DSL,最终仍编译为 .so 文件;pybind11 是 C++ 绑定库,底层仍依赖 Python C API;rust-cpython 或 PyO3 同理。这些工具降低了门槛,但没改变“扩展必须符合 CPython ABI”这一事实。
容易被忽略的一点:CPython 版本升级可能破坏二进制兼容性。例如 PyLong_AsLong 在 3.12 中行为微调,或某些内部宏(如 Py_SIZE)在调试模式下含义变化——这意味着你写的 C 扩展必须针对目标 Python 版本重新编译,且不能假设 ABI 稳定。










