Python 的 C API 是 Python 解释器提供的一组 C 语言函数、宏和数据结构
Python 的 C API 是 Python 解释器提供的一组 C 语言函数、宏和数据结构,用于在 C/C++ 代码中嵌入 Python 解释器、扩展 Python 功能(编写 C 扩展模块)、与 Python 对象交互,或实现高性能底层操作。它是 Python 实现(CPython)的核心接口,仅对 CPython 保证稳定(其他实现如 PyPy、Jython 不兼容此 API)。
关键特性包括:
- 对象模型接口:所有 Python 对象(
PyObject*)通过统一的 C 结构体表示,支持引用计数(Py_INCREF/Py_DECREF)、类型检查(PyLong_Check)、转换(PyLong_AsLong)等。 - 模块定义机制:通过
PyModuleDef结构和PyInit_modulename()函数导出 C 函数为 Python 可调用函数。 - GIL(全局解释器锁)管理:
PyGILState_Ensure()/PyGILState_Release()用于多线程 C 扩展中安全访问 Python API。 - 错误处理:通过
PyErr_SetString()设置异常,PyErr_Occurred()检测异常,C 函数返回NULL表示异常。 - 内存管理:推荐使用
PyMem_Malloc/PyObject_Malloc而非标准malloc,以兼容 Python 内存分配器。 - 初始化与清理:
Py_Initialize()(已弃用,推荐Py_InitializeEx(0)或PyConfig初始化)、Py_FinalizeEx()(现代推荐配合Py_RunMain使用)。
⚠️ 注意:自 Python 3.12 起,官方大力推动「稳定 ABI」(pybind11、cpython crate 等依赖),鼓励使用 Python.h 中标记为 PyAPI_FUNC 且属于稳定 ABI 的函数(避免直接访问结构体字段,如 ob_refcnt 已被封装为宏)。
示例:最简 C 扩展函数(example.c)
#include <Python.h>
static PyObject* example_add(PyObject* self, PyObject* args) {
long a, b;
if (!PyArg_ParseTuple(args, "ll", &a, &b)) {
return NULL; // 触发 TypeError
}
return PyLong_FromLong(a + b);
}
static PyMethodDef ExampleMethods[] = {
{"add", example_add, METH_VARARGS, "Add two integers"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef examplemodule = {
PyModuleDef_HEAD_INIT,
"example",
"An example C extension",
-1,
ExampleMethods
};
PyMODINIT_FUNC PyInit_example(void) {
return PyModule_Create(&examplemodule);
}
编译需 setup.py 或 pyproject.toml(使用 setuptools + Extension),并链接 python3.x 库。
在 C 扩展中安全释放 GIL(Global Interpreter Lock)是实现真正并行 CPU 计算的关键,尤其适用于计算密集型任务(如数值处理、图像编码、加密等)。释放 GIL 后,C 代码可并发执行,但不得调用任何 Python C API 函数(包括访问/创建/修改 PyObject*),否则将导致崩溃或未定义行为。
✅ 正确流程(四步法):
-
保存并释放 GIL
使用PyThreadState* save = PyThreadState_Get(); PyThreadState_Swap(NULL); PyEval_ReleaseLock();(旧式,已弃用)❌
✅ 推荐方式(Python 3.2+):PyThreadState* _save; // ① 释放 GIL(自动保存线程状态) _save = PyThreadState_Swap(NULL); PyEval_ReleaseThread(_save); // Python 3.9+ 推荐用 PyThreadState_EnterTracing() + PyThreadState_LeaveTracing()?不,应使用更现代的宏✅ 最简、最安全、官方推荐写法(Python 3.2+):
PyThreadState* _save = PyThreadState_Get(); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); // ❌ 已废弃(3.12 移除)✅ 正确且现代的方式(Python 3.2–3.11):
PyThreadState* _save; _save = PyThreadState_Get(); PyThreadState_Swap(NULL); PyEval_ReleaseLock(); // ⚠️ 仅适用于 <3.12;3.12+ 必须用 Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS 宏✅ 终极推荐:使用宏(全版本兼容、自动处理异常恢复)
long result; Py_BEGIN_ALLOW_THREADS // ← 自动释放 GIL 并保存线程状态 // 🔒 此处严禁调用任何 Python C API! result = heavy_computation(a, b); // 纯 C 计算,无 PyObject 操作 Py_END_ALLOW_THREADS // ← 自动重新获取 GIL 并恢复线程状态 -
在无 GIL 区域严格隔离 Python API
- ✅ 允许:纯 C 运算、malloc/free、系统调用(read/write)、OpenMP/MPI、调用其他 C/C++ 库(如 NumPy C API 的 non-Python-facing 部分需额外注意)
- ❌ 禁止:
PyLong_FromLong,PyList_Append,PyObject_GetAttrString,PyErr_SetString,Py_INCREF等一切以Py或PyObject_开头的函数,以及访问ob_refcnt、ob_type等结构体字段。
-
GIL 恢复后处理结果并返回 Python 对象
Py_BEGIN_ALLOW_THREADS result = compute_expensive_task(data, size); Py_END_ALLOW_THREADS // ✅ GIL 已恢复,现在可安全调用 Python API if (result == -1) { PyErr_SetString(PyExc_RuntimeError, "Computation failed"); return NULL; } return PyLong_FromLong(result); -
异常安全:若计算中发生信号或错误,需确保 GIL 被恢复
Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS是成对的宏,底层使用setjmp/longjmp或 RAII 式清理(CPython 内部实现),能保证即使longjmp或siglongjmp触发,GIL 也会被自动恢复 —— 这是手动调用PyEval_ReleaseLock()/PyEval_AcquireLock()无法保证的。
📌 补充技巧:
- 若需在释放 GIL 期间与 Python 交互(如进度回调),必须重新获取 GIL 临时(用
PyGILState_Ensure()/PyGILState_Release()),但会牺牲并行性,慎用。 - 多线程 C 扩展中,建议用
PyGILState_Ensure()初始化线程状态(尤其非 Python 创建的线程)。 - 使用
#define NO_PYTHON_API编译时检查(非标准,需自定义)可辅助静态检测误调用。
✅ 总结口诀:
“宏进宏出,中间禁 Py;计算归 C,结果回 Python。”
更多推荐


所有评论(0)