Python C扩展模块实例:example
在C中编写Python扩展模块的流程涉及多个核心步骤:
- 包含Python头文件
- 编写C函数逻辑
- 定义模块方法表
- 创建模块定义结构
- 实现模块初始化函数
一.文件名examplemodule.c
`#include
// 1. 实现C函数(Python可调用的函数)
static PyObject example_add(PyObject self, PyObject *args){
int num1, num2;
// 解析两个整数参数
if (!PyArg_ParseTuple(args, "ii", &num1, &num2)) {
returnNULL; // 解析失败时返回异常
}
return PyLong_FromLong(num1 + num2); // 返回Python整数对象
}
// 2. 定义模块方法表
static PyMethodDef ExampleMethods[] = {
{"add", example_add, METH_VARARGS, "Add two integers."},
{NULL, NULL, 0, NULL} // 哨兵值,标记结束
};
// 3. 定义模块结构
staticstructPyModuleDefexamplemodule = {
PyModuleDef_HEAD_INIT,
"example", // 模块名
"Example module documentation.",
-1, // 全局状态(-1表示不使用GIL)
ExampleMethods
};
// 4. 模块初始化函数(必须命名为PyInit_<模块名>)
PyMODINIT_FUNC PyInit_example(void){
return PyModule_Create(&examplemodule);
}
`
1.example_add()函数
这段代码实现了一个 Python C 扩展模块中的 add 函数。其功能如下:
static PyObject *example_add(PyObject *self, PyObject *args){ int num1, num2; if (!PyArg_ParseTuple(args, "ii", &num1, &num2)) { returnNULL; } return PyLong_FromLong(num1 + num2); }
example_add是一个 C 函数,供 Python 调用。- 参数
self是模块自身(未使用),args是传入的参数元组。 - 使用
PyArg_ParseTuple(args, "ii", &num1, &num2)解析传入的两个整数参数,分别赋值给num1和num2。 - 如果解析失败,返回
NULL,表示出错。 - 如果成功,返回
num1 + num2的和,类型为 Python 的长整型对象(PyLong_FromLong)。
该函数实现了 Python 层的 add(a, b),返回两个整数的和。
2.Python C扩展模块的方法表
这段代码定义了一个 Python C 扩展模块的方法表。具体说明如下:
// 模块方法表 static PyMethodDef ExampleMethods[] = { {"add", example_add, METH_VARARGS, "Add two integers."}, {NULL, NULL, 0, NULL} };
static PyMethodDef ExampleMethods[]:声明一个方法表数组,用于注册模块中的所有方法。- 每个元素是一个结构体,包含四个字段:
- 方法名(如
"add"),即 Python 中调用的函数名。 - C 函数指针(如
example_add),实际实现该方法的 C 函数。 - 调用方式(如
METH_VARARGS),表示参数以元组形式传递。 - 方法的文档字符串(如
"Add two integers.")。
- 方法名(如
- 最后一个元素必须全为
NULL或0,表示方法表结束。
3.PyModuleDef结构体
examplemodule 是一个 PyModuleDef 结构体,用于定义 Python 扩展模块的元数据。
// 模块定义 staticstructPyModuleDefexamplemodule = { PyModuleDef_HEAD_INIT, "example", "Example module documentation.", -1, ExampleMethods };
各字段含义如下:
PyModuleDef_HEAD_INIT:结构体初始化宏,必须写。"example":模块名称。"Example module documentation.":模块文档字符串。-1:模块的状态大小,-1 表示全局状态(不需要为每个解释器单独分配内存)。ExampleMethods:模块中定义的方法表。
该结构体用于 PyModule_Create,从而生成 Python 可用的扩展模块对象。
4.Python C扩展模块的初始化函数
这段代码是 Python C 扩展模块的初始化函数,即这段代码实现了Python3扩展模块的标准初始化入口。
// 模块初始化函数 PyMODINIT_FUNC PyInit_example(void){ return PyModule_Create(&examplemodule); }
PyMODINIT_FUNC PyInit_example(void)是模块初始化函数,名称必须为PyInit_模块名,用于 Python 3 动态加载模块时调用。PyModule_Create(&examplemodule)创建并返回一个新的 Python 模块对象,examplemodule是模块的定义结构体。- 该函数返回模块对象指针,供 Python 解释器加载模块时使用。
二.编译与安装流程
1. 创建setup.py
`from setuptools import setup, Extension
module = Extension(
'example', # 模块名
sources=['examplemodule.c'], # C源文件
)
setup(
name='example',
version='1.0',
description='Example C extension',
ext_modules=[module],
)
`
2. 编译并安装
`# 安装到当前Python环境
python setup.py install
或直接编译(生成.so/.pyd文件)
python setup.py build_ext --inplace
`
如果执行python setup.py install,那么安装到d:\python310\lib\site-packages,如下所示:


如果执行python setup.py build_ext --inplace,那么生成example.cp310-win_amd64.pyd:

3.在Python中使用
import example print(example.add(3, 7)) # 输出: 10

三.关键概念详解
1.函数签名
static PyObject* func(PyObject *self, PyObject *args)args:包含所有参数的元组,需用PyArg_ParseTuple解析
2.参数解析
PyArg_ParseTuple(args, "ii", &a, &b):解析两个整数- 格式字符串:
"i"(int),"f"(float),"s"(string)等
3.返回值处理
- 返回Python对象:
PyLong_FromLong(),PyFloat_FromDouble() - 错误时返回
NULL并设置异常(如PyErr_SetString)
4.引用计数
- Python对象需手动管理引用
- 使用
Py_INCREF/Py_DECREF(简单函数中通常不需要)
四.添加main方法
要在 examplemodule.c 中添加 main 方法,使其既能作为 Python 扩展模块使用,又能作为独立可执行程序运行,可以通过条件编译实现。以下是完整代码:
`#include
include // 添加标准输入输出头文件
// 实现C函数(Python可调用的函数)
static PyObject example_add(PyObject self, PyObject *args){
int num1, num2;
// 解析两个整数参数
if (!PyArg_ParseTuple(args, "ii", &num1, &num2)) {
returnNULL; // 解析失败时返回异常
}
return PyLong_FromLong(num1 + num2);
}
// 定义模块方法表
static PyMethodDef ExampleMethods[] = {
{"add", example_add, METH_VARARGS, "Add two integers."},
{NULL, NULL, 0, NULL} // 结束标记
};
// 定义模块结构
staticstructPyModuleDefexamplemodule = {
PyModuleDef_HEAD_INIT,
"example", // 模块名
"Example module documentation.",
-1, // 全局状态
ExampleMethods
};
// 模块初始化函数
PyMODINIT_FUNC PyInit_example(void){
return PyModule_Create(&examplemodule);
}
/**** 使用Python C API的main函数 ****/
intmain(){
printf("Running as standalone program using Python C API\n");
// 初始化Python解释器
Py_Initialize();
// 创建模块对象
PyObject *pModule = PyModule_Create(&examplemodule);
if (!pModule) {
PyErr_Print();
return1;
}
// 将模块添加到sys.modules
PyObject *sys_modules = PyImport_GetModuleDict();
PyDict_SetItemString(sys_modules, "example", pModule);
// 调用模块中的add函数
PyObject pFunc = PyObject_GetAttrString(pModule, "add");
if (pFunc && PyCallable_Check(pFunc)) {
// 准备参数 (5, 7)
PyObject pArgs = PyTuple_Pack(2, PyLong_FromLong(5), PyLong_FromLong(7));
// 调用函数
PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
if (pValue) {
// 获取并打印结果
long result = PyLong_AsLong(pValue);
printf("5 + 7 = %ld\n", result);
// 清理引用
Py_DECREF(pValue);
} else {
PyErr_Print();
}
// 清理引用
Py_DECREF(pArgs);
Py_DECREF(pFunc);
} else {
PyErr_Print();
}
// 清理模块引用
Py_DECREF(pModule);
// 关闭Python解释器
Py_Finalize();
return0;
}
`
CMakeLists.txt文件,如下所示:
`cmake_minimum_required(VERSION 3.25)
project(CPythonExample C)
set(CMAKE_C_STANDARD 11)
include python headers files
include_directories(
/usr/local/opt/python-3.14.0b1/include/python3.14
/usr/local/opt/python-3.14.0b1/include/python3.14/internal
/usr/local/opt/python-3.14.0b1/include/python3.14/cpython
)
Link python library
link_directories(/usr/local/opt/python-3.14.0b1/lib)
include python headers files
include_directories(
/mnt/l/20200707_Python/PythonSource/cpython
/mnt/l/20200707_Python/PythonSource/cpython/Include
/mnt/l/20200707_Python/PythonSource/cpython/Include/internal
/mnt/l/20200707_Python/PythonSource/cpython/Include/cpython
)
Link python library
link_directories(/mnt/l/20200707_Python/PythonSource/cpython)
add_executable(CPythonExample 0003-example/examplemodule.c
Python/pythonrun.c
Python/pylifecycle.c
Python/ceval.c
Python/pyarena.c
0003-example/examplemodule.c)
Define Py_BUILD_CORE
target_compile_definitions(CPythonExample PRIVATE Py_BUILD_CORE)
link python library
target_link_libraries(CPythonExample python3.14 m)
`
1.作为独立程序编译运行
`# 编译
gcc examplemodule.c -o example
运行
./example
`
输出结果,如下所示:

Running as standalone program using Python C API 5 + 7 = 12
2. 作为Python扩展模块使用
`# 使用setup.py编译安装
python setup.py build_ext --inplace
在Python中使用
import example
print(example.add(5, 7)) # 输出 12
`
这种实现方式允许同一份代码文件:
- 作为高性能 Python 扩展模块
- 作为独立的 C 程序测试核心逻辑
- 避免 Python 环境依赖影响核心算法测试
- 方便进行单元测试和性能基准测试
五.相关技术点
1.Py_Initialize()
用于初始化 Python 解释器。调用此函数后,C 程序可以使用 Python C API 执行 Python 代码、创建对象等。必须在使用任何 Python API 之前调用,且在程序结束时应调用 Py_Finalize(); 关闭解释器。
2.PyModule_Create()
PyModule_Create 是 Python C API 中用于创建新的模块对象的函数。它的原型如下:
PyObject* PyModule_Create(PyModuleDef *module);
- 参数
module是一个指向PyModuleDef结构体的指针,该结构体定义了模块的名称、文档字符串、方法表等信息。 - 调用
PyModule_Create会根据PyModuleDef的内容创建并返回一个新的 Python 模块对象(PyObject*)。这个对象可以被添加到 Python 解释器的模块字典中,从而在 Python 代码中导入和使用。
3.PyImport_GetModuleDict()
// 将模块添加到sys.modules PyObject *sys_modules = PyImport_GetModuleDict(); PyDict_SetItemString(sys_modules, "example", pModule);
PyImport_GetModuleDict是 Python C API 的一个函数,用于获取当前解释器的模块字典(等同于 Python 里的sys.modules),这个字典保存了所有已加载模块的名称和模块对象的映射。PyDict_SetItemString是用于操作 Python 字典对象的 C API 函数。它的作用是将一个键值对插入到指定的 Python 字典中,键为 C 字符串,值为PyObject*。在上述代码中,它用于把自定义模块对象添加到sys.modules字典,使得该模块可以被 Python 代码访问和导入。
4.PyObject_GetAttrString()
在该代码中,它用于从模块对象 pModule 获取名为 "add" 的函数对象:
PyObject *pFunc = PyObject_GetAttrString(pModule, "add");
PyObject_GetAttrString 是 Python C API 中的一个函数,用于获取指定 Python 对象的某个属性。原型:
PyObject *PyObject_GetAttrString(PyObject *o, constchar *attr_name);
参数说明:
o:目标 Python 对象(如模块、类、实例等)。attr_name:属性名(C 字符串)。
返回值:
- 成功时返回属性对应的
PyObject*指针(需手动Py_DECREF)。 - 失败时返回
NULL,并设置异常。
5.PyCallable_Check(pFunc)
用于判断 pFunc 是否是一个可调用对象(如函数、方法、类等)。如果 pFunc 可以像函数一样被调用,则返回非零值(True),否则返回0(False)。这通常用于在调用对象前确保其可调用,避免运行时错误。
6.PyTuple_Pack和PyObject_CallObject
`// 准备参数 (5, 7)
PyObject *pArgs = PyTuple_Pack(2, PyLong_FromLong(5), PyLong_FromLong(7));
// 调用函数
PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
`
PyTuple_Pack用于创建一个新的 Python 元组对象,并将传入的 C 变量作为元组的元素。例如,PyTuple_Pack(2, PyLong_FromLong(5), PyLong_FromLong(7))会生成一个包含 5 和 7 的元组(5, 7)。PyObject_CallObject用于调用一个可调用的 Python 对象(如函数),第一个参数是要调用的对象,第二个参数是传递给该对象的参数(通常是元组)。例如,PyObject_CallObject(pFunc, pArgs)会调用pFunc,并将pArgs作为参数传递。
7.Py_DECREF和Py_INCREF
Py_DECREF 和 Py_INCREF 是 Python C API 中用于管理对象引用计数的宏。
Py_INCREF(obj):将obj的引用计数加 1,表示又有一个地方持有了该对象的引用。Py_DECREF(obj):将obj的引用计数减 1,如果引用计数变为 0,则自动释放该对象占用的内存。
它们用于防止内存泄漏和悬挂指针,确保 Python 对象的生命周期被正确管理。
8.Py_Finalize()
Py_Finalize() 是 Python C API 中用于关闭 Python 解释器的函数。它会清理所有 Python 资源、释放内存、关闭打开的文件和模块,并使 Python 解释器进入未初始化状态。调用后,不能再使用任何 Python C API,除非重新调用 Py_Initialize()。通常在嵌入式 Python 程序结束前调用。
参考文献
[0] Python C扩展模块实例:example:https://z0yrmerhgi8.feishu.cn/wiki/CRdBwu8dsiAicakGjxTcR26fn6g
[1] 使用C或C++扩展Python:https://docs.python.org/zh-cn/3.13/extending/extending.html
知识星球服务内容:Dify源码剖析及答疑,Dify对话系统源码,NLP电子书籍报告下载,公众号所有付费资料。加微信buxingtianxia21进NLP工程化资料群。
(文:NLP工程化)