在C中编写Python扩展模块的流程涉及多个核心步骤:
-
包含Python头文件
-
编写C函数逻辑
-
定义模块方法表
-
创建模块定义结构
-
实现模块初始化函数
一.文件名examplemodule.c
#include<Python.h>
// 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<Python.h>
#include<stdio.h> // 添加标准输入输出头文件
// 实现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工程化)