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

如何在C扩展中将自定义C数据结构与Python模块对象绑定存储

模块私有C数据绑定最佳实现

方案1:使用官方模块状态API(推荐)

Python 3.5+提供的原生模块状态机制就是为C扩展模块存储私有C数据设计的,无需依赖私有API,也不用担心Python层修改数据,是当前场景的最优解。

实现步骤

  1. 定义模块状态结构体,存储你的私有C数据
struct cheese_module_state {
    struct cheese_data* data;
    // 可按需添加其他私有字段
};
  1. 定义PyModuleDef时指定状态大小和释放回调
// 模块释放回调,模块销毁时自动触发
static void cheese_module_free(PyObject* module) {
    struct cheese_module_state* state = PyModule_GetState(module);
    if (state && state->data) {
        // 调用你的C数据释放逻辑
        cheese_data_destroy(state->data);
        state->data = NULL;
    }
}

static PyMethodDef cheese_module_methods[] = {
    // 你的模块方法定义,按需补充
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef PyCheeseModuleDef = {
    PyModuleDef_HEAD_INIT,
    .m_name = "custom_cheese", // 可动态修改为对应cheese文件名
    .m_doc = "Cheese format module",
    .m_size = sizeof(struct cheese_module_state), // 关键:指定状态结构体大小
    .m_methods = cheese_module_methods,
    .m_free = cheese_module_free, // 绑定释放回调
};
  1. 模块初始化阶段填充状态数据
    创建模块后,通过PyModule_GetState获取状态指针,写入初始化完成的C数据即可:
// 加载cheese文件生成模块的逻辑
PyObject* create_cheese_module(const char* cheese_path) {
    // 按需构造ModuleSpec,也可以直接用PyModule_Create
    PyObject* module = PyModule_FromDefAndSpec(&PyCheeseModuleDef, spec);
    if (!module) return NULL;

    struct cheese_module_state* state = PyModule_GetState(module);
    if (!state) {
        Py_DECREF(module);
        return NULL;
    }

    // 调用你的C库逻辑加载cheese数据
    state->data = cheese_data_load(cheese_path);
    if (!state->data) {
        PyErr_SetString(PyExc_RuntimeError, "Failed to load cheese file");
        Py_DECREF(module);
        return NULL;
    }

    if (PyModule_ExecDef(module, &PyCheeseModuleDef) < 0) {
        Py_DECREF(module);
        return NULL;
    }

    return module;
}

方案优势

  • 完全使用公开Python/C API,无兼容性风险
  • 状态数据仅C层可访问,Python层无法读写,完全规避篡改风险
  • 生命周期自动管理,模块销毁时自动触发释放逻辑
  • 原生支持多解释器场景,每个模块实例拥有独立状态

方案2:Capsule封装兼容旧版本

如果需要兼容Python 3.5以前的版本,可以用无公开接口的Capsule封装C数据,存入模块私有属性:

// 加载完cheese数据后封装为Capsule,绑定释放函数
PyObject* data_cap = PyCapsule_New(cheese_data, "cheese_private_data", 
    (PyCapsule_Destructor)cheese_data_destroy);
if (!data_cap) {
    cheese_data_destroy(cheese_data);
    return NULL;
}
// 以双下划线开头的私有属性名存入模块,正常用户不会修改
if (PyModule_AddObjectRef(module, "__cheese_private", data_cap) < 0) {
    Py_DECREF(data_cap);
    return NULL;
}
Py_DECREF(data_cap);

模块被销毁时,Capsule会被同步回收,自动触发绑定的释放函数,实现成本极低。


针对之前尝试方案的补充说明

  • 动态分配PyModuleDef完全合规:官方没有限制PyModuleDef必须静态定义,每个cheese文件可以生成独立的动态PyModuleDef,只需在m_free回调中顺便释放PyModuleDef本身的内存即可,不会出现内存泄漏。
  • 无需继承PyModuleObject:模块状态API已经完全覆盖了自定义模块存储私有数据的需求,不需要使用未暴露的私有结构体。

内容的提问来源于stack exchange,提问作者David Z

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 20:45:04