You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Python C API在C扩展模块中实现闭包功能?

Python C API 实现闭包的完整方案

要实现和Python原生闭包完全一致的效果,我们可以利用Python C API中PyCFunction可绑定自定义上下文的特性,将外部捕获的变量作为返回函数的self上下文存储,以下是完整实现步骤:


1. C扩展核心代码(mymodule.c)

#include <Python.h>

// 闭包内add函数的C实现,self就是捕获的外部变量x
static PyObject* add(PyObject* self, PyObject* args) {
    PyObject* y;
    // 解析传入的参数
    if (!PyArg_ParseTuple(args, "O", &y)) {
        return NULL;
    }
    // 直接调用Python原生加法,支持所有实现了__add__的类型
    return PyNumber_Add(self, y);
}

// add函数的方法定义
static PyMethodDef AddMethodDef = {
    .ml_name = "add",
    .ml_meth = add,
    .ml_flags = METH_VARARGS,
    .ml_doc = "Add captured x with input y"
};

// 导出的get_add函数的C实现
static PyObject* mymodule_get_add(PyObject* self, PyObject* args) {
    PyObject* x;
    if (!PyArg_ParseTuple(args, "O", &x)) {
        return NULL;
    }
    // 增加x的引用计数,避免被提前回收
    Py_INCREF(x);
    // 创建绑定了x作为上下文的可调用函数对象
    PyObject* add_func = PyCFunction_NewEx(&AddMethodDef, x, NULL);
    // PyCFunction已经持有x的引用,释放本地引用避免泄漏
    Py_DECREF(x);
    return add_func;
}

// 模块方法表
static PyMethodDef MyModuleMethods[] = {
    {
        "get_add",
        mymodule_get_add,
        METH_VARARGS,
        "Return a closure that adds input value with captured x"
    },
    {NULL, NULL, 0, NULL} // 哨兵结束标记
};

// 模块定义结构体
static struct PyModuleDef mymodulemodule = {
    PyModuleDef_HEAD_INIT,
    .m_name = "mymodule",
    .m_doc = "Demo module for closure implementation with Python C API",
    .m_size = -1,
    .m_methods = MyModuleMethods
};

// 模块初始化函数
PyMODINIT_FUNC PyInit_mymodule(void) {
    return PyModule_Create(&mymodulemodule);
}

2. 编译配置(setup.py)

from setuptools import setup, Extension

setup(
    name="mymodule",
    version="1.0",
    description="Python C API closure demo",
    ext_modules=[Extension("mymodule", sources=["mymodule.c"])]
)

执行以下命令完成编译,编译完成后当前目录会生成对应平台的扩展模块文件(.so/.pyd):

python setup.py build_ext --inplace

3. 功能测试

测试代码和效果和原生Python完全一致:

from mymodule import get_add

add5 = get_add(5)
add10 = get_add(10)
print(add5(12)) # 输出17
print(add10(12)) # 输出22
# 也支持其他可相加类型,比如字符串
add_hello = get_add("hello ")
print(add_hello("world")) # 输出hello world

注意事项

  • 捕获外部变量时必须主动调用Py_INCREF增加引用计数,PyCFunction销毁时会自动对绑定的上下文执行Py_DECREF,不提前增加引用会导致变量被提前回收引发野指针错误。
  • 本实现直接调用PyNumber_Add做加法,和原生Python闭包的行为完全一致,支持所有实现了__add__魔术方法的Python类型。

内容的提问来源于stack exchange,提问作者Vipul Cariappa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 04:48:04