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」(pybind11cpython 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.pypyproject.toml(使用 setuptools + Extension),并链接 python3.x 库。

在 C 扩展中安全释放 GIL(Global Interpreter Lock)是实现真正并行 CPU 计算的关键,尤其适用于计算密集型任务(如数值处理、图像编码、加密等)。释放 GIL 后,C 代码可并发执行,但不得调用任何 Python C API 函数(包括访问/创建/修改 PyObject*),否则将导致崩溃或未定义行为。

✅ 正确流程(四步法):

  1. 保存并释放 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 并恢复线程状态
    
  2. 在无 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 等一切以 PyPyObject_ 开头的函数,以及访问 ob_refcntob_type 等结构体字段。
  3. 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);
    
  4. 异常安全:若计算中发生信号或错误,需确保 GIL 被恢复
    Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS成对的宏,底层使用 setjmp/longjmp 或 RAII 式清理(CPython 内部实现),能保证即使 longjmpsiglongjmp 触发,GIL 也会被自动恢复 —— 这是手动调用 PyEval_ReleaseLock() / PyEval_AcquireLock() 无法保证的。

📌 补充技巧:

  • 若需在释放 GIL 期间与 Python 交互(如进度回调),必须重新获取 GIL 临时(用 PyGILState_Ensure() / PyGILState_Release()),但会牺牲并行性,慎用。
  • 多线程 C 扩展中,建议用 PyGILState_Ensure() 初始化线程状态(尤其非 Python 创建的线程)。
  • 使用 #define NO_PYTHON_API 编译时检查(非标准,需自定义)可辅助静态检测误调用。

✅ 总结口诀:

“宏进宏出,中间禁 Py;计算归 C,结果回 Python。”
在这里插入图片描述

更多推荐