使用C/C++ 扩展Python,实现Python模块扩展或嵌入Python解释器
Python扩展开发。是使用C/C++来编写Python模块,通过导入动态链接库,调用C/C++编写的模块
嵌入Python解释器。在编写C/C++的程序时,通过调用Python解释器来执行Python的代码
嵌入Python解释器
编写Demo程序
Py_Initialize 初始化Python解释器
PyRun_SimpleString 执行简单的python语句,输出
hello world
Py_Finalize 释放或销毁解释器
1#include <Python.h>
2
3int main (int argc, char *argv[])
4{
5 Py_Initialize();
6 PyRun_SimpleString("print('hello world')");
7 Py_Finalize();
8 return 0;
9}
编译以及执行程序
1g++ test.cpp -o test \
2 -I/Library/Frameworks/Python.framework/Versions/3.7/include/python3.7m \
3 -L/Library/Frameworks/Python.framework/Versions/3.7/lib/ \
4 -lpython3.7m
5
6./test
思考
如何实现一个动态的执行程序?
通过读取Python文件,将文件内容通过传参的方式交付给`PyRun_SimpleString`,这就回归到`C/C++`中的文件读取的问题了
我们可以通过程序传参或者重定向的方式,将 `print('hello world')` 替换为我们想要执行的程序
是否可以实现代码热更新呢?
通常情况下,我们会耗费大量的时间在编译大型的C/C++程序,如果通过动态加载Python代码的方式,达到我们想要的效果,大大地提升了我们的效率,虽然这会牺牲程序的性能。
开发Python内置模块
PyObject* add(PyObject* self, PyObject* args)
定义一个静态的方法,返回数据结构PyObject*
在C Python库中,所有的数据类型都为 `PyObject*`
PyArg_ParseTuple
解析函数add
的传参`ii` 表示传入两个 int 类型的数值
`s` 表示传入的一个字符串参数
详细说明可以查看参考引用。1
MyDemoMethods 为方法生命数组,定义模块方法名,绑定方法,以及方法注释等
demomodule 定义模块,模块名称为
demo
PyMODINIT_FUNC PyInit_demo(void)
初始化创建模块
扩展模块开发
1// demo.cpp
2#include "Python.h"
3
4static PyObject* add(PyObject* self, PyObject* args){
5 int a, b;
6 if (!PyArg_ParseTuple(args, "ii", &a, &b)){
7 return nullptr;
8 }
9 int sum = a + b;
10 PyObject* ret = PyLong_FromLong(sum);
11 return ret;
12}
13
14static PyMethodDef MyDemoMethods[] = {
15 {"addx", add, METH_VARARGS, "add two integers"},
16 {nullptr, nullptr, 0, nullptr},
17};
18
19static struct PyModuleDef demomodule = {
20 PyModuleDef_HEAD_INIT,
21 "demo", /* name of module */
22 NULL, /* module documentation, may be NULL */
23 -1, /* size of per-interpreter state of the module,
24 or -1 if the module keeps state in global variables. */
25 MyDemoMethods
26};
27
28PyMODINIT_FUNC PyInit_demo(void){
29 return PyModule_Create(&spammodule);
30}
编写python程序,调用动态库模块,执行程序
sys.path.append('./demo.so')
加载动态链接库demo.addx
执行模块demo
中的方法addx
1# test.py
2import sys
3
4sys.path.append('./demo.so')
5
6import demo
7
8
9def main():
10 print(demo.addx(123, 4312))
11
12
13if __name__ == "__main__":
14 main()
编译以及执行程序
1g++ -fpic -c \
2 -I/Library/Frameworks/Python.framework/Versions/3.7/include/python3.7m \
3 demo.cpp
4
5g++ -shared -o demo.so demo.o -lstdc++ \
6 -L/Library/Frameworks/Python.framework/Versions/3.7/lib/ -lpython3.7m
7
8python test.py
9# output: 4435
思考
通过C/C++编写Python模块的好处良多。直接调用C/C++编写的程序大大地提升了Python应用程序的性能。
总结
无论用C/C++编写Python的模块,还是内置Python解释器,最终的解决方案都需要我们自己评估。需要从应用场景,开发成本,性能、或者效率提升等方面抉择,最终落地。
通过底层的学习,让我们更深入地了解Python的实现原理以及应用,在编写Python程序时候会更加注意细节。
资料引用
[1] PyArg_ParseTuple 参数解析