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)