Python C 扩展

参考文档

简述

最近了解了一下Python C 扩展技术,这个技术十分地有用,可以通过C/C++语言为Python赋能,进一步地,通过C/C++调用其他编程语言的过程也是成熟的,因此也可以通过这个方法来调用其他编程语言。当然,这个技术(Python C扩展)本身的一个重要的作用是给原生性能一般的Python编写高性能外部库(例如numpy),通过Python调用高性能的计算库来完成计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
// main.c
#define PY_SSIZE_T_CLEAN
#include <python3.13/Python.h>
static struct PyModuleDef Module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "MyModule",
.m_size = 0, // non-negative
.m_slots = NULL,
.m_methods = NULL
};
PyMODINIT_FUNC PyInit_test(void){
return PyModuleDef_Init(&Module);
}
1
2
gcc main.c -shared -o test.so -LPython3
python
1
>>> import test

通过以上过程,我们就完成了一个Python扩展模块框架的搭建。

新建一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// main.c
#define PY_SSIZE_T_CLEAN
#include <python3.13/Python.h>
static PyObject* teststr(PyObject* self, PyObject* args){
return PyUnicode_FromString("Hello,World!");
}
static PyMethodDef methods[] = {
{"teststr", teststr, METH_NOARGS, "Retrun a str"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef Module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "MyModule",
.m_size = 0, // non-negative
.m_slots = NULL,
.m_methods = methods
};
PyMODINIT_FUNC PyInit_test(void){
return PyModuleDef_Init(&Module);
}
1
2
gcc main.c -shared -o test.so -LPython3
python
1
2
3
>>> import test
>>> test.teststr()
'Hello,World!'

需要通过static PyMethodDef methods[]定义一个模块方法数组,这个数组暴露出去的就是Python解释器可以调用的这个模块导出的外部函数。

1
2
3
4
5
6
struct PyMethodDef {
const char *ml_name; /* 方法/函数名称 */
PyCFunction ml_meth; /* 负责实现这个函数的C语言函数 */
int ml_flags; /* 通过宏中METH_XXXX这样的flags组合得到的一个整数,表明函数传入参数类型 */
const char *ml_doc; /* Python内__doc__属性。如果是NULL在Python中就是None */
};

其他

通过Python C扩展,也可以实现一些乱七八糟的玩意儿。举个例子,如果我们预期制作一个完整的软件,并且可以通过整合Python作为脚本语言完成一些事情。(具体来说,比如,做一个编程驱动的游戏)这样我们只需要将Python需要调用的接口通过Python C扩展写好,然后用户就可以通过Python代码调用、二次开发了。(某种程度上,这个属于嵌入Python解释器到一个更大的应用程序的范畴)