c++调用python脚本

深度学习相关的算法,都是使用python语言编写;
应用工程等基本由c++编写;想要将两者很好的结合起来,会缩短开发时间。

一、环境的设置:

编写一个Python脚本:Script1.py
(一开始命名Script.py。这样的命名有坑,会导致后面的程序运行出现问题。后面文件命名要避开这种关键词,然后文件名由Script.py 改成Script1.py)

import tensorflow  ##测试这个库的影响
#!/usr/bin/python
# Filename: script.py
class Student:
    def SetName(self,name):
        self._name = name
    def PrintName(self):
        print(self._name)
def hello():
    print ("Hello World\n")
def world(name):
    print ("name")

然后创建一个工程,编写主函数进行调用这个python脚本:main.c++

#include 
#include 
#include 
#include  
int main() {
    Py_SetPythonHome(L"\\Python366");
    # 添加的python的整个完成的环境包的路劲,必须添加。
    
    //使用python之前,要调用Py_Initialize();这个函数进行初始化
    Py_Initialize();
        if (Py_IsInitialized())
	    std::cout << "Init Success" << std::endl;
    
    PyRun_SimpleString("import sys");             #在python初始化之后
    PyRun_SimpleString("sys.path.append('./')");   #""里面填写的是python的语言
    PyRun_SimpleString("print(sys.version)\n");

    PyObject * pModule = NULL;
    PyObject * pFunc = NULL;
    PyObject * pClass = NULL;
    PyObject * pInstance = NULL;
    
    //这里是要调用的文件名
    pModule = PyImport_ImportModule("Script1");
    //这里是要调用的函数名
    pFunc = PyObject_GetAttrString(pModule, "hello");
    //调用函数
    PyEval_CallObject(pFunc, NULL);
    Py_DECREF(pFunc);
    
    pFunc = PyObject_GetAttrString(pModule, "world");
    PyObject_CallFunction(pFunc, "s", "zhengji");
    Py_DECREF(pFunc);
    
    //测试调用python的类
    pClass = PyObject_GetAttrString(pModule, "Student");
    if (!pClass) {
        printf("Can't find Student class.\n");
        return -1;
    }
    pInstance = PyInstance_New(pClass,Null,Null); // python2的使用语法 
    pInstance = PyInstanceMethod_New(pClass);    //python3的
    if (!pInstance) {
        printf("Can't create Student instance.\n");
        return -1;
    }
    PyObject_CallMethod(pInstance, "SetName", "s", "my family");
    PyObject_CallMethod(pInstance, "PrintName", NULL, NULL);
    //调用Py_Finalize,这个根Py_Initialize相对应的。
    Py_Finalize();
    return 0;
}

然后将系统环境中的python的include、lib和dll进行工程属性的环境配置。即可正常运行

C:\Users\hp\AppData\Local\Programs\Python\Python35\include
C:\Users\hp\AppData\Local\Programs\Python\Python35\libs\python35.lib

注意问题:

  1. Py_SetPythonHome设置库的路径,打包时必须使用(网上的例子较少提到)。并且在python初始化之前。将系统系的完整的python环境包,或者conda的虚拟环境(建议)完整的包,拷出到工程路径下,然后设置该路径,这样工程才能正确的调用python的环境 。这样工程可以在没有安装python的电脑上运行
  2. 使用conda创建的虚拟环境。这样安装的tensorflow-gpu所使用的cuda也存在于虚拟环境中。使用系统的python打包时候比较麻烦,容易遗漏安装在系统下的其它文件,比如cudnn.dll。
  3. 系统环境python2或者python3要和python脚本的语法相符。提前验证,python脚本能在当前的python下成功运行。

 

二、c++调用Python的API:

1.设置Python环境的路径

Py_SetPythonHome(L"\\Python366");

2.初始化和关闭python解释器

#include
Py_Initialize();
.... # (所有需要执行的python程序)
Py_Finalize();

3.load python模块

分为两种方式

(1)直接Load一个python写好的文件(假设文件名叫pytest.py,在当前目录下)

PyObject *pName, *pMoudule;
PyRun_SimpleString("import sys"); //导入系统模块
PyRun_SimpleString("sys.path.append('./')");
PyRun_SimpleString("print(sys.version)");

pName = PyString_FromString("pytest");
pModule = PyImport_Import(pName);  // 或者直接  pModule = PyImport_Import("pytest");

(2)Load一个已经安装好的Python模块 (假设模块名叫numpy)

PyObject* pModule;
pModule = PyImport_Import(PyString_FromString(“numpy”));
//pModule = PyImport_Import(“numpy”);

4 从pModule模块里load需要调用的函数并执行

两种方式

(1)从模块里load一个函数(假设函数名叫func,有一个int型的输入变量,返回一个int型的整数)

PyObject *pfunc, *args, *result;
pfunc = PyObject_GetAttrString(pModule, "func")  
args = Py_BuildValue("i", 12345);  //设置调用func时的输入变量,这里假设12345
result = PyObject_CallObject(pfunc, args);  //执行func(12345),并将结果返回给result

2)从模块里load一个类,然后调用类内部的函数(假设类名叫Executor,初始化需要当前文件夹下的配置文件config.txt。函数名叫func)

PyObject *pClass, *pDict, *pInStance, *class_args, *result;
pDict = PyModule_GetDict(pModule);   //拿到pModule里的所有的类和函数的定义
pClass = PyDict_GetItemString(pDict, "Executor");  //找到名为Executor的类
class_args = Py_BuildValue("(s)", "./config.txt"); //设置类初始化需要的参数
pInstance = PyInstance_New(pClass, class_args, NULL);  //初始化Executor,建立实例pInstance
result = PyObject_CallMethod(pInstance, "func", "(i)", 12345);  //执行pInstance.func(12345)

5 c++向Python传递参数

因为在Python中,所有的类型都经过了一层封装,导致c++的参数需要做一个类型转换,转换成PyObject* 才能传入Python。例如c++的一个int就是个整数,该值占用8bytes(64位)的存储空间,而一个Python的int实际是一个PyObject* 指向24bytes。前8个bytes是个整数,代表引用次数,中间的8bytes是个指向int类型定义的指针,最后8bytes才是这个int的值。所以c++和Python之间参数的互相传递都需要Python提供的API。
假设函数的输入变量有三个,分别为一个整数(i),一个浮点数(f)和一个字符串(s)

PyObject* args = PyTuple_New(3);
PyObject* arg1 = Py_BuildValue("i",100);  //整数参数
PyObject* arg2 = Py_BuildValue("f", 3.14);  //浮点数参数
PyObject* arg3 = Py_BuildValue("s", "hellow"); //字符串参数
PyTuple_SetItem(args, 0, arg1);
PyTuple_SetItem(args, 0, arg2);
PyTuple_SetItem(args, 0, arg3);

// 以上的函数可简化为
PyObject* args = Py_BuildValue("ifs", 100, 3.14, "hellow");

如果输入参数是另一个python函数的输出结果PyObject* results (o)

PyObject* args = Py_BuildValue("(0)", results);

6 解析python的返回值PyObject* result

同c++传递参数到python类似,调用解析函数
单个返回值: PyArg_Parse()
多返回值: PyArg_ParseTuple()

7 编译执行

vs配好相关环境,编译运行即可

https://zhuanlan.zhihu.com/p/25888215

三、实际应用举例

int main() {

	vector> corr_list;

	Py_SetPythonHome(L"Python_env");
	//使用python之前,要调用Py_Initialize();这个函数进行初始化
	Py_Initialize();
	if (Py_IsInitialized())
		std::cout << "Init Success" << std::endl;
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./')");
	PyRun_SimpleString("print(sys.version)\n");

	PyObject * pModule = NULL;
	PyObject * pFunc = NULL;
	PyObject * pDict = NULL;
	PyObject * pReturn = NULL;

	//这里是要调用的文件名
	pModule = PyImport_ImportModule("Script1");
	pFunc = PyObject_GetAttrString(pModule, "szSecTest");
	long out_vert[2][3] = { { 2,3,4 },{ 5,6,7 } };

	PyObject *PyList = PyList_New(0);//定义该PyList对象为0和PyList_Append有关,相当于定义PyList为[],存放输入数据
	PyObject *ArgList = PyTuple_New(1); 
	for (int i = 0; i <2; i++) {
		PyObject *PyList1 = PyList_New(3);
		for (int j = 0; j < 3; j++) {
			PyList_SetItem(PyList1, j, PyFloat_FromDouble(out_vert[i][j]));
		}
		PyList_Append(PyList, PyList1);//PyList_Append可以参考Python中,list的append的用处
	}
	PyTuple_SetItem(ArgList, 0, PyList);//将PyList对象放入PyTuple对象中
	pReturn = PyObject_CallObject(pFunc, ArgList);//调用函数,返回一个list

	if (PyList_Check(pReturn)) { //检查是否为List对象
		corr_list.clear();
		vector corr_sub;

		int SizeOfList = PyList_Size(pReturn);//List对象的大小,这里SizeOfList = 2
		cout <<"output list len: "<< SizeOfList << endl;
		PyObject *ListItem=NULL;
		PyObject *Item=NULL;
		for (int i = 0; i < SizeOfList; i++) {
			ListItem = PyList_GetItem(pReturn, i);//获取List对象中的每一个元素
			int NumOfItems = PyList_Size(ListItem);//List对象子元素的大小,这里NumOfItems = 2
			corr_sub.clear();
			for (int j = 0; j < NumOfItems; j++) {
				Item = PyList_GetItem(ListItem, j);//遍历List对象中子元素中的每个元素
				int result;
				PyArg_Parse(Item, "i", &result);//i表示转换成int型变量
				corr_sub.push_back(result);
			}
			corr_list.push_back(corr_sub);
		}
		Py_DECREF(ListItem); //释放空间
		Py_DECREF(Item); //释放空间
	}
	else { cout << "Not a List" << endl; }

	Py_DECREF(pFunc);
	Py_Finalize();
	cout << "python_env endding......................" << endl;
	return 0;
}

Script1.py

def szSecTest(List):
    print (List)
 
    IntegerList = []
    for i in range(100):
        IntegerList.append([1, 2, 3])
    return IntegerList

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(环境配置)