使用 C++ 调用 Python 脚本,并实现各种类型参数的传递。

前言

C++ 可以把 Python 当成文本形式的动态链接库,需要的时候可以改一改脚本,不需要编译。调 Python 提供了丰富的 C API 函数,通过使用自带的API接口可以实现对 Python 脚本文件中的函数、类的访问以及调用。从而可以使得我们可以方便的使用 Python 代码快速得实现一些额外的需求。比如使用 Terson Flow 进行开发。

本文主要介绍 C++ 对 Python 函数的调用,C++ 对 Python 类的调用以及 C++ 与 Python 之间数组的相互传递。

C++ 对 Python 简单函数的调用

通过调用 python 脚本中的函数,来实现一些简单的功能扩展。先编写一个 Python 脚本文件名为 pytest.py。在此之前需要安装 python 环境。


def Add(a,b):  
    print "in python function add"  
    print "a = " + str(a)  
    print "b = " + str(b)  
    print "ret = " + str(a+b)  
    return a+b

下面是 C++ 代码对其进行调用,在编译时需要加入 Python 的 include 路径和动态库的库接路径。

LIBS +=  -L/usr/lib/python2.7/config-x86_64-linux-gnu -lpython2.7
INCLUDEPATH += /usr/include/python2.7

# include <Python.h>

int main( int argc, char * argv[] ) {

    // 在使用Python系统前,必须使用Py_Initialize
    Py_Initialize();

    // 检查初始化是否成功
    if ( !Py_IsInitialized() ) {
        return -1;
    }

    // 载入Python的内建模块并添加系统路径
    PyRun_SimpleString( "import sys" );
    PyRun_SimpleString( "print '---import sys---'" );
    PyRun_SimpleString( "sys.path.append('./')" );    
    
    PyObject * pName   = NULL;
    PyObject * pModule = NULL;
    PyObject * pDict   = NULL;
    PyObject * pFunc   = NULL;
    PyObject * pArgs   = NULL;

    // 载入名为pytest的脚本
    pName   = PyString_FromString( "pytest" );
    pModule = PyImport_Import( pName) ;
    if ( !pModule ) {
        printf("can't find pytest.py");
        getchar();
        return -1;
    }

    pDict = PyModule_GetDict(pModule);
    if ( !pDict ) {
        return -1;
    }
    
    // 找出函数名为add的函数
    printf("----------------------\n");
    pFunc = PyDict_GetItemString( pDict, "Add" );
    if ( !pFunc || !PyCallable_Check( pFunc ) ) {
        printf("can't find function [add]");
        getchar();
        return -1;
     }

    // 参数进栈
    *pArgs;
    pArgs = PyTuple_New(2);

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

    PyTuple_SetItem( pArgs, 0, Py_BuildValue( "l", 3 ) );
    PyTuple_SetItem( pArgs, 1, Py_BuildValue( "l", 4 ) );

    // 调用Python函数
    PyObject * pyRet = PyObject_CallObject( pFunc, pArgs );

    // 获取返回值
    int reval = -1;
    int retok = PyArg_Parse( pyRet, "i", &reval );
    printf( "返回结果: %d\n", reval );

    Py_DECREF( pModule );
    Py_DECREF( pDict );
    Py_DECREF( pFunc );
    Py_DECREF( pArgs );
    Py_DECREF( pName );

    return 0;
}

C++ 对 Python 类的成员函数的调用

Python 代码如下:


class tester:  
    def add(self, a, b):  
        return a + b

C++ 代码调用时调用如下


// 调用类
// 从字典中查找tester类
PyObject * pyCls = PyDict_GetItemString( pDict, "tester" );
// 创建一个tester类
PyObject * PyIns = PyInstance_New( pyCls, NULL, NULL );
// 调用tester.add方法,传入2个int型参数
pyRet = PyObject_CallMethod( PyIns, "add", "ii", 5, 6 );
retok = PyArg_Parse( pyRet, "i", &reval );
printf( "调用类的函数,返回结果: %d\n", reval );

从 Python 函数中返回List数组

如果我们在 C/C++ 文件中调用的 Python 函数返回的是 List 数组,那么我们这里主要用到的是 Python C API 中的 List Object 来处理返回的数据,主要用到 List Object 里面的这些函数:

  1. int PyList_Check( PyObject * list ) 函数判断一个 PyObject 指针对象是不是一个 List 对象;

  2. Py_ssize_t PyList_Size( PyObject * list ) 函数计算一个 List 对象的大小;

  3. PyObject * PyList_GetItem( PyObject * list, Py_ssize_t index ) 函数返回 List对象中第 index 个元素的 PyObject 指针。

Python 代码如下


def testList():
    IList = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    return IList;

在 C/C++ 文件中调用这个 Python 函数时,将返回 List 对象,在 C/C++ 文件中进行如下的接收操作:


// 省略…… 
PyObject * pFun     = PyDict_GetItemString( pDict, "testList" );
PyObject * pFunRet   = PyObject_CallObject( pFun, nullptr );//返回List对象

if( PyList_Check( pFunRet ) ) { //检查是否为List对象

    int sizeOfList = PyList_Size( pFunRet ); //list的长度
    for( idx = 0; idx < sizeOfList; idx++ ) {
        // 获取 List 对象中的每一个元素
        PyObject * listItem = PyList_GetItem( pFunRet, idx );

        // List对象子元素的大小,这里NumOfItems = 3
        int NumOfItems = PyList_Size( listItem );
        for( Index_k = 0; Index_k < NumOfItems; Index_k++ ){

            //遍历List对象中子元素中的每个元素 
            PyObject * item = PyList_GetItem( listItem, Index_k );

            cout << PyInt_AsLong( item ) <<" "; //输出元素

            Py_DECREF( Item ); //释放空间
        }

        Py_DECREF( ListItem ); //释放空间
    }

    cout<<endl;

}
else {
    cout<<"Not a List"<<endl;
}

// 省略……

C++ 向 Python 函数中返回List数组

现在我们的需求是我们要将 C/C++文件中的数组传递给 Python 文件的某个函数,那么我们将借助 List Object 和 Tuple Object 来封装我们的数据,从而传递给 Python 文件中的函数。

Python 代码如下:


def PrintList( l ):
    print l;

在 C++ 文件里调用如下:


double CArray[] = { 1.2, 4.5, 6.7, 8.9, 1.5, 0.5 };

// 定义一个与数组等长的PyList对象数组
PyObject * PyList  = PyList_New( 6 );

// 定义一个Tuple对象,Tuple对象的长度与Python函数参数个数一直,
// 上面Python参数个数为1,所以这里给的长度为1
PyObject * ArgList = PyTuple_New( 1 );

for( i = 0; i < PyList_Size( PyList ); ++i ){
    PyList_SetItem( PyList, Index_i, PyFloat_FromDouble( CArray[i] ) );
}

PyObject * pFuncFour = PyDict_GetItemString( pDict, "PrintList" );
cout << "C Array Pass Into The Python List:" << endl;

PyTuple_SetItem( ArgList, 0, PyList );      // 将PyList对象放入PyTuple对象中
PyObject_CallObject( pFuncFour, ArgList );  // 调用函数,完成传递

comment

Comments are closed.