Qt(C++)与python混编经验记录

简述

刚好遇到了需求, 研究了下python和C/C++的混合编程. 对比了原生API和Cython, SWIG这类扩展库, 最终还是决定采用原生API方案.

实现

  1. 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #include <Python.h>
    int Widget::pow_function_from_python(int a, int b) {
    Py_Initialize();//初始化python解释器
    if ( !Py_IsInitialized() ) return -1;//检查初始化

    int res;
    PyObject *pModule,*pFunc;
    PyObject *pArgs, *pValue;

    pModule = PyImport_Import(PyUnicode_FromString("math"));//导入模块
    pFunc = PyObject_GetAttrString(pModule, "pow");//获取函数
    if ( !pFunc || !PyCallable_Check(pFunc) ) return -1;//检查函数是否存在

    //构造参数
    pArgs = PyTuple_New(2);
    PyTuple_SetItem(pArgs,0, Py_BuildValue("i",a));
    PyTuple_SetItem(pArgs,1, Py_BuildValue("i",b));

    //调用函数
    pValue = PyObject_CallObject(pFunc, pArgs);
    //处理返回值
    res = PyLong_AsLong(pValue);

    Py_Finalize();
    return res;
    }
  • 所有Python元素,module、function、tuple、string等等,实际上都是PyObject。C语言里操纵它们,一律使用PyObject *。
  • Python的类型与C语言类型可以相互转换。Python类型XXX转换为C语言类型YYY要使用PyXXX_AsYYY函数;
  • C类型YYY转换为Python类型XXX要使用PyXXX_FromYYY函数。
  • 也可以创建Python类型的变量,使用PyXXX_New可以创建类型为XXX的变量。
  • 若a是Tuple,则a[i] = b对应于 PyTuple_SetItem(a,i,b),有理由相信还有一个函数PyTuple_GetItem完成取得某一项的值。
  • 不仅Python语言很优雅,Python的库函数API也非常优雅。
  1. 头文件
    1
    #include <Python.h>

注意: python.h 中定义了一些预处理宏, 可能与其他标准头文件冲突, 故必须将python.h 在其他头文件之前包含. 在Qt中, python的object.h使用slot作为变量名, 与Qt的宏定义冲突, 可如下修改避免.

1
2
3
#undef slots     		//这里取消slots宏定义
PyType_Slot *slots; /* terminated by slot==0. */
#define slots Q_SLOTS //这里再恢复

  1. 函数介绍
    void Py_Initialize()
    初始化python解释器. 此函数应该在所有python API函数使用前被调用(Py_SetProgramName(), Py_SetPythonHome() 及 Py_SetPath()例外), 重复调用时为空操作.

Py_IsInitialized()
返回python解释器初始化状态. 强烈建议检查初始化状态, 未初始化情况下直接调用API函数将引起程序崩溃, 且无任何调试信息.

int PyRun_SimpleString()
执行一行简单的python代码, 成功返回0, 失败返回-1, 执行失败无调试信息.

Py_Finalize()
取消所有python解释器的初始化, 回收资源.

PyImport_Import()
导入模块, 对应python代码中的import.

PyUnicode_FromString()
将C字符串转化为python Unicode字符串.(仅python3支持).

PyObject_GetAttrString()
获取python函数.

PyTuple_New
构造python元组.

PyTuple_SetItem()
设置元组变量.

PyObject_CallObject()
调用python函数.

PyLong_AsLong
转换python中的long类型数据为C语言long型.

Py_BuildValue()
把C++的变量转换成一个Python对象。当需要从C++传递变量到Python时,就会使用这个函数。此函数有点类似C的printf,但格式不同。常用的格式有
s 表示字符串
i 表示整型变量
f 表示浮点数
O 表示一个Python对象.

  1. 编译
    需添加python的头文件和库文件 在Qt中具体做法为在pro文件中添加:
    1
    2
    INCLUDEPATH += -I C:\Users\AndyF\AppData\Local\Programs\Python\Python36-32\include
    LIBS += -LC:\Users\AndyF\AppData\Local\Programs\Python\Python36-32\libs -lpython36

在VisualStudio下, 可用命令进行编译

1
cl my_python.c -IC:\Python27\include C:\Python27\libs\python27.lib

在linux下, 编译命令为

1
gcc my_python.c -o my_python -I/usr/include/python2.7/ -lpython2.7

参考文献

c++与Python3互相调用
QT与Python混合编程经验记录
在c中内嵌python编程
如何实现 C/C++ 与 Python 的通信?
C++嵌入Python程序(二):参数传入及返回
C++调用Python浅析