C++调用python

41 阅读6分钟

简述

  • 主流方法将python程序编程文本形式的动态链接库,在c/c++程序中调用其中定义的函数
  • 本质上是在 c++ 中启动了一个 python 解释器,由解释器对 python 相关的代码进行执行,执行完毕后释放资源,达到调用目的

实现

链接到Python调用库
  • 检查Python 安装目录下已经包含头文件( 就是c++里 需要include 的目录)和库文件 ( Windows 下为 python27.lib)
直接调用 Python 语句
#include "python/Python.h"

int main()
{
    Py_Initialize();

    PyRun_SimpleString("print 'hello'");

    Py_Finalize();
}
无参python函数调用
  • python
def say():
    print("hello")
  • C++
#include <Python.h>
#include <iostream>
 
using namespace std;
 
int main(){
    // 1、初始化python接口  
    Py_Initialize();
    if(!Py_IsInitialized()){
        cout << "python init fail" << endl;
        return 0;
    }
    
    // 2、初始化python系统文件路径,保证可以访问到 .py文件
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./script')");

    // 3、调用python文件名,不用写后缀
    PyObject* pModule = PyImport_ImportModule("sayhello");
    if( pModule == NULL ){
        cout <<"module not found" << endl;
        return 1;
    }
        
    // 4、检查函数是否可以调用
    PyObject* pFunc = PyObject_GetAttrString(pModule, "say");
    if( !pFunc || !PyCallable_Check(pFunc)){
        cout <<"not found function add_num" << endl;
        return 0;
    }
       
    // 5、调用函数
    PyObject_CallObject(pFunc, NULL);
    
    // 5、结束python接口初始化
    Py_Finalize();
    return 0;
}
有参python函数调用
  • python
# myadd.py
def AdditionFc(a, b):
    print("Now is in python module")
    print("{} + {} = {}".format(a, b, a+b))
    return a + b
  • C++
#include<Python.h> 
#include <iostream>
using namespace std;
 
int main()
{
    // 1、初始化python接口
    Py_Initialize(); 
    
    // 初始化使用的变量
    PyObject* pModule = NULL;
    PyObject* pFunc = NULL;
    PyObject* pName = NULL;
    
    // 2、初始化python系统文件路径,保证可以访问到 .py文件
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");
    
    // 3、调用python文件名。当前的测试python文件名是 myadd.py
    // 在使用这个函数的时候,只需要写文件的名称就可以了。不用写后缀。
    pModule = PyImport_ImportModule("myadd");
    
    // 4、检查函数是否可以调用
    pFunc = PyObject_GetAttrString(pModule, "AdditionFc");
    if( !pFunc || !PyCallable_Check(pFunc)){
        cout <<"not found function AdditionFc" << endl;
        return 0;
    }
    
    // 5、给python传参数
    // 函数调用的参数传递均是以元组的形式打包的,2表示参数个数
    // 如果AdditionFc中只有一个参数时,写1就可以了
    PyObject* pArgs = PyTuple_New(2);
 
    // 5.0:第一个参数,传入 int 类型的值 2
    PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 2)); 
    // 5.1:第二个参数,传入 int 类型的值 4
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 4)); 
    
    // 6、使用C++的python接口调用该函数
    PyObject* pReturn = PyEval_CallObject(pFunc, pArgs);
    
    // 7、接收python计算好的返回值
    // i表示转换成int型变量。
    // 在这里,最需要注意的是:PyArg_Parse的最后一个参数,必须加上“&”符号
    int nResult;
    PyArg_Parse(pReturn, "i", &nResult);
    cout << "return result is " << nResult << endl;
    
    //8、结束python接口初始化
    Py_Finalize();
}
编译
# g++ main.cpp -i /usr/include/python3.7 -lpython3.7m -o run

API

// 初始化Python解释器,如果初始化失败,继续下面的调用会出现各种错误,可惜的是此函数没有返回值来判断是否初始化成功,如果失败会导致致命错误。
void Py_Initialize(void)

// 检查是否已经进行了初始化,如果返回0,表示没有进行过初始化。
int Py_IsInitialized(void)

// 反初始化Python解释器,包括子解释器,调用此函数同时会释放Python解释器所占用的资源。
void Py_Finalize()

// 实际上是一个宏,执行一段Python代码。
int PyRun_SimpleString(const char *command)

// 导入一个Python模块,参数name可以是*.py文件的文件名。类似Python内建函数import。
PyObject* PyImport_ImportModule(char *name)

// 相当于Python模块对象的dict属性,得到模块名称空间下的字典对象。
PyObject* PyModule_GetDict( PyObject *module)

// 执行一段Python代码。
PyObject* PyRun_String(const char* str, int start,PyObject* globals, PyObject* locals)

// 把Python数据类型解析为C的类型,这样C程序中才可以使用Python里面的数据。
int PyArg_Parse(PyObject* args, char* format, …)

// 返回模块对象o中的attr_name 属性或函数,相当于Python中表达式语句,o.attr_name。
PyObject* PyObject_GetAttrString(PyObject *o, char*attr_name)

// 和PyArg_Parse刚好相反,构建一个参数列表,把C类型转换为Python对象,使得Python里面可以使用C类型数据。
PyObject* Py_BuildValue(char* format, …)

// 此函数有两个参数,而且都是Python对象指针,其中pfunc是要调用的Python 函数,一般说来可以使用PyObject_GetAttrString()获得,pargs是函数的参数列表,通常是使用Py_BuildValue()来构建。
PyObject* PyEval_CallObject(PyObject* pfunc, PyObject*pargs)

例子

////////////////////////////////////////////////////////
// 1. 传送字符串和数据到python函数
////////////////////////////////////////////////////////

/*
python文件: demo1.py

def Hello(s):
    print("\n=======================")
    print("demo1:hello")
    print(s)


def Add(a, b):
    print("\n=======================")
    print("demo1:add")
    print("{0}".format(a + b))
*/

void TestF1(void) 
{
    PyObject* pModule = NULL;
    PyObject* pFunc = NULL;
    PyObject* pArg = NULL;
    pModule = PyImport_ImportModule("demo1"); //注意文件名字大小写
    pFunc = PyObject_GetAttrString(pModule, "Hello");//获取函数名称
    pArg = Py_BuildValue("(s)", "my is c++ test!");  //一个字符串参数
    PyEval_CallObject(pFunc, pArg);//函数调用
    pFunc = PyObject_GetAttrString(pModule, "Add");  //两个整形的参数
    pArg = Py_BuildValue("(i,i)", 10, 25);  // 变量格式转换成python格式
    PyEval_CallObject(pFunc, pArg);
}
  

///////////////////////////
// 2.列表作为参数的传送给python函数
////////////////////////////

/*
python文件:demo2.py

def printList(l):
    print("\n=======================")
    print("demo2")
    print(len(l))
    print(l);
*/

void TestF2(void)
{
    PyObject* pModule = NULL;
    PyObject* pFunc = NULL;
    PyObject* pArg = NULL;

    pModule = PyImport_ImportModule("demo2"); //注意文件名字大小写
    PyObject* pyFunc_printList = PyObject_GetAttrString(pModule, "printList");//获取函数名称
    if (pModule && PyCallable_Check(pyFunc_printList))
    {
        PyObject* pyParams = PyList_New(0);           //初始化一个列表
        PyList_Append(pyParams, Py_BuildValue("d", 5));//列表添加元素值浮点数
        PyList_Append(pyParams, Py_BuildValue("i", 2));
        PyList_Append(pyParams, Py_BuildValue("i", 6));
        PyList_Append(pyParams, Py_BuildValue("i", 8));
        PyObject* args = PyTuple_New(1);              //定义一个python变量
        PyTuple_SetItem(args, 0, pyParams);// 变量格式转换成python格式
        PyEval_CallObject(pyFunc_printList, args);//函数调用
    }
}


/////////////////////////////////
/////////////////////////////////
// 3.python类的操作
/////////////////////////////////

/*
python文件:demo3.py

class Person:
       def __init__(self, name):
            self.name = name
       def printName(self):
           print("\n=======================")
           print("demo3")
           print (self.name)
*/

void TestF3(void) 
{
    PyObject* pModule = NULL;
    PyObject* pFunc = NULL;
    PyObject* pArg = NULL;
    PyObject* pClass = NULL;
    PyObject* pObject = NULL;

    pModule = PyImport_ImportModule("demo3");               //注意文件名字大小写
    pClass = PyObject_GetAttrString(pModule, "Person");     //先获取类名
    pArg = PyTuple_New(1);  // 定义一个变量                                 
    PyTuple_SetItem(pArg, 0, Py_BuildValue("s", "Class:Jacky"));  // 变量格式转换成python格式
    pObject = PyEval_CallObject(pClass, pArg);              //根据类名实例化对象
    pFunc = PyObject_GetAttrString(pObject, "printName");   //根据对象得到成员函数
    PyEval_CallObject(pFunc, NULL);//函数调用
}


/////////////////////////////////
/////////////////////////////////
// 4. Python程序返回参数的例子
/////////////////////////////////

/*
python文件:demo4.py

def mix(a,b):
    print("\n=======================")
    print("demo4")       
    r1 = a + b
    r2 = a - b
    return (r1, r2)
*/

void TestF4(void)
{
    PyObject* pModule = PyImport_ImportModule("demo4");//注意文件名字大小写
    PyObject* pyFunc_mix = PyObject_GetAttrString(pModule, "mix");
    if (pModule && PyCallable_Check(pyFunc_mix))
    {
        PyObject* pyParams = PyTuple_New(2); //定义两个变量
        PyTuple_SetItem(pyParams, 0, Py_BuildValue("i", 5));// 变量格式转换成python格式
        PyTuple_SetItem(pyParams, 1, Py_BuildValue("i", 2));// 变量格式转换成python格式
        int r1 = 0, r2 = 0;
        PyObject* pyValue = PyObject_CallObject(pyFunc_mix, pyParams); //调用函数返回结果
        PyArg_ParseTuple(pyValue, "i|i", &r1, &r2);//分析返回的元组值
        if (pyValue)
        {
            printf("%d   %d\n", r1, r2);
        }
    }

}


///////////////////
// 5.c++数组转python的list
/////////////////////

/*
python文件:demo5.py

def printList(lis):
    print("\n=======================")
    print("demo5")
    print(lis)
*/

void TestF5(void)
{
    //以下调用很重要,否则转换中会出现异常
    if (_import_array() < 0) 
    {

        PyErr_Print(); 

        PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); 
    } 

   
    PyObject* pModule = NULL;
    PyObject* pFunc = NULL;

    pModule = PyImport_ImportModule("demo5");//注意文件名字大小写
    PyObject* pyFunc_printList = PyObject_GetAttrString(pModule, "printList");//获取函数名称

    float buf[2][3]; //定义二维数组
    buf[0][0] = 0;
    buf[0][1] = 1.233;
    buf[0][2] = 2.222;
    buf[1][0] = 4.222;
    buf[1][1] = 5.333;
    buf[1][2] = 6.333;

    PyObject* pArgs = PyTuple_New(1);
    npy_intp dims[2]={2,3}; //定义list的shape
    int ND = 2;//指明list的维度
    PyObject* pPyArray = PyArray_SimpleNewFromData(ND, dims, NPY_FLOAT, buf); //指明list的维度,shape,数量类型,缓冲区
    PyTuple_SetItem(pArgs, 0, pPyArray);//变量转换
    PyEval_CallObject(pyFunc_printList, pArgs);//函数调用
 }