如何创建包含多个子模块的Python C扩展模块?
Python C扩展单共享库实现子模块方案
核心思路
不需要拆分多个共享库,只需在单个主模块初始化函数中完成以下操作:
- 创建主模块对象
- 分别创建子模块(
subpart1、subpart2)对象 - 将子模块对应的类、函数注册到子模块中
- 将子模块作为属性添加到主模块
最终编译为单个共享库,即可实现my_custom_module.subpart1这类语法访问。
完整实现示例
1. C扩展代码(my_custom_module.c)
#include <Python.h> // -------------------------- subpart1 子模块内容 -------------------------- // ClassB 的类型定义 static PyObject* ClassB_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { PyObject* self = type->tp_alloc(type, 0); return self; } static PyTypeObject ClassBType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "my_custom_module.subpart1.ClassB", .tp_basicsize = sizeof(PyObject), .tp_flags = Py_TPFLAGS_DEFAULT, .tp_new = ClassB_new, }; // subpart1 子模块的方法表(可选) static PyMethodDef Subpart1Methods[] = { {"func_b", NULL, METH_VARARGS, "Subpart1 test function"}, {NULL, NULL, 0, NULL} // 结束标记 }; // subpart1 子模块定义 static struct PyModuleDef subpart1_module = { PyModuleDef_HEAD_INIT, .m_name = "my_custom_module.subpart1", .m_doc = "Subpart1 module", .m_size = -1, .m_methods = Subpart1Methods, }; // -------------------------- subpart2 子模块内容 -------------------------- // ClassC 的类型定义 static PyObject* ClassC_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { PyObject* self = type->tp_alloc(type, 0); return self; } static PyTypeObject ClassCType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "my_custom_module.subpart2.ClassC", .tp_basicsize = sizeof(PyObject), .tp_flags = Py_TPFLAGS_DEFAULT, .tp_new = ClassC_new, }; // subpart2 子模块定义 static struct PyModuleDef subpart2_module = { PyModuleDef_HEAD_INIT, .m_name = "my_custom_module.subpart2", .m_doc = "Subpart2 module", .m_size = -1, }; // -------------------------- 主模块初始化 -------------------------- PyMODINIT_FUNC PyInit_my_custom_module(void) { PyObject* main_module; PyObject* sub1_module; PyObject* sub2_module; // 1. 创建并初始化主模块 static struct PyModuleDef main_module_def = { PyModuleDef_HEAD_INIT, .m_name = "my_custom_module", .m_doc = "Main custom module", .m_size = -1, }; main_module = PyModule_Create(&main_module_def); if (!main_module) goto error; // 2. 创建并初始化 subpart1 子模块 sub1_module = PyModule_Create(&subpart1_module); if (!sub1_module) goto error; // 将 ClassB 注册到 subpart1 if (PyType_Ready(&ClassBType) < 0) goto error; Py_INCREF(&ClassBType); PyModule_AddObject(sub1_module, "ClassB", (PyObject*)&ClassBType); // 3. 创建并初始化 subpart2 子模块 sub2_module = PyModule_Create(&subpart2_module); if (!sub2_module) goto error; // 将 ClassC 注册到 subpart2 if (PyType_Ready(&ClassCType) < 0) goto error; Py_INCREF(&ClassCType); PyModule_AddObject(sub2_module, "ClassC", (PyObject*)&ClassCType); // 4. 将子模块添加到主模块 PyModule_AddObject(main_module, "subpart1", sub1_module); PyModule_AddObject(main_module, "subpart2", sub2_module); return main_module; error: // 错误清理:减少引用计数并返回 NULL Py_XDECREF(main_module); Py_XDECREF(sub1_module); Py_XDECREF(sub2_module); return NULL; }
2. 编译配置(setup.py)
from setuptools import setup, Extension setup( name="my_custom_module", version="0.1", ext_modules=[ Extension( "my_custom_module", sources=["my_custom_module.c"], ) ] )
3. 编译与测试
- 编译安装:执行
pip install .或python setup.py install - 测试代码:
import my_custom_module # 访问子模块的类 b_instance = my_custom_module.subpart1.ClassB() c_instance = my_custom_module.subpart2.ClassC() print(type(b_instance)) # <class 'my_custom_module.subpart1.ClassB'> print(type(c_instance)) # <class 'my_custom_module.subpart2.ClassC'>
关键细节说明
- 子模块的
tp_name必须包含完整路径(如my_custom_module.subpart1.ClassB),否则Python会认为类属于主模块 - 所有子模块的创建和注册逻辑都在
PyInit_my_custom_module中完成,最终只生成一个my_custom_module.so(或对应平台的共享库文件) - 使用
Py_XDECREF进行错误清理,避免内存泄漏
内容的提问来源于stack exchange,提问作者Nicolas V
相关产品推荐
相关产品推荐

