C++ 调用python方法

主流方法将python程序编程文本形式的动态链接库,在c/c++程序中调用其中定义的函数。

本质上是在 c++ 中启动了一个 python 解释器,由解释器对 python 相关的代码进行执行,执行完毕后释放资源,达到调用目的,具体步骤如下:

1. 链接到Python调用库

检查Python 安装目录下已经包含头文件( 就是c++里 需要include 的目录)和库文件 ( Windows 下为 python27.lib)。
这一步可以参考下知乎网友的配置方式 https://zhuanlan.zhihu.com/p/271219435

2. 直接调用 Python 语句

#include "python/Python.h"

int main()
{
    Py_Initialize();    ## 初始化

    PyRun_SimpleString("print 'hello'");

    Py_Finalize();      ## 释放资源
}

3. 无参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;
}
    // 
    PyObject_CallObject(pFunc, NULL);
    // 5、结束python接口初始化
Py_Finalize();
return 0;
}

编译

4. 有参python函数调用

python

# myadd.py
def AdditionFc(a, b):
    print("Now is in python module")
    print("{} + {} = {}".format(a, b, a+b))
    return a + b

C++

// test2.cpp
#include<Python.h> 
#include <iostream>
using namespace std;
 
int main()
{
    Py_Initialize(); //1、初始化python接口
    
    //初始化使用的变量
    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");
    
    //5、给python传参数
    // 函数调用的参数传递均是以元组的形式打包的,2表示参数个数
    // 如果AdditionFc中只有一个参数时,写1就可以了
    PyObject* pArgs = PyTuple_New(2);
 
    // 0:第一个参数,传入 int 类型的值 2
    PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 2)); 
    // 1:第二个参数,传入 int 类型的值 4
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 4)); 
    
    // 6、使用C++的python接口调用该函数
    PyObject* pReturn = PyEval_CallObject(pFunc, pArgs);
    
    // 7、接收python计算好的返回值
    int nResult;
    // i表示转换成int型变量。
    // 在这里,最需要注意的是:PyArg_Parse的最后一个参数,必须加上“&”符号
    PyArg_Parse(pReturn, "i", &nResult);
    cout << "return result is " << nResult << endl;
    
    //8、结束python接口初始化
    Py_Finalize();
}

编译

方法二:直接调用一个python脚本文件。

(这个方法可能取返回值不方便)

以下是测试用的python脚本文件,功能是输出命令行参数:sample.py

# !/usr/bin/env python2.6
import sys
def test():
    for arg in sys.argv:
        print arg

if __name__=='__main__':
    test()

以下是测试用的c程序文件:test.c

#include "python2.6/Python.h"
int main()
{
    //第一步:初始化Python
    //在调用Python的提供的给C的API之前,通过执行初始化
    //来添加Python的内建模块、__main__、sys等
    Py_Initialize();

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

    //第二步:导入sys模块
    PyRun_SimpleString("import sys");

    //第三步:导入执行脚本时的命令行参数,如:./sample.py int argc = 2;   
char *argv[2];  
argv[0] = "arg1";   
argv[1] = "arg2";
PySys_SetArgv(argc, argv);

    //第四步:执行调用脚本文件命令,注意文件的路径
    if (PyRun_SimpleString("execfile('./sample.py')") == NULL)
    {
        return -1;
    }

    //第五步:关闭Python解释器
    Py_Finalize();
    return 0;
}

编译指令:

g++ -g -W -o test test.cpp -I /usr/include/ -L /usr/lib64/ -l python2.
执行指令:./test
输出:

arg1
arg2

Python提供C++调用的主要的API

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

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

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

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

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

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

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

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

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

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

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

参数传递的各种例子

////////////////////////////////////////////////////////
//1. 传送字符串和数据到python函数
////////////////////////////////////////////////////////
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);
}

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))
  
///////////////////////////
//2.列表作为参数的传送给python函数
////////////////////////////
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);//函数调用
    }
}

python文件:demo2.py
def printList(l):
print("\n=======================")
print("demo2")
print(len(l))
print(l);
/////////////////////////////////
/////////////////////////////////
//3.python类的操作
/////////////////////////////////
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);//函数调用
}


python文件:demo3.py

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


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

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);
        }
    }

}

python文件:demo4.py

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

///////////////////
//5.c++数组转python的list
/////////////////////
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);//函数调用
 }


python文件:demo5.py

def printList(lis):
print("\n=======================")
print("demo5")
print(lis)
posted @ 2022-09-29 18:40  MasonLee  阅读(4010)  评论(0编辑  收藏  举报