深度学习相关的算法,都是使用python语言编写;
应用工程等基本由c++编写;想要将两者很好的结合起来,会缩短开发时间。
实际过程中,会把python的脚本编写成类的形式。然后使用C++对python类进行实例化、实例对象的成员函数调用。1 环境配置
电脑环境:windows10、vs2015、python3.6
【python3.6】
- 安装anaconda,使用conda创建虚拟环境,并在虚拟环境中配置好自己需要的资源。
- 找到安装anaconda路径下,创建的虚拟环境的文件夹,我的电脑在默认的路径下【C:\Users\XXX\Anaconda3\envs】。其中 XXX是自己的电脑的名字。
- 将自己的虚拟环境拷贝,到创建的VS工程下。
【vs2015】
- 【新建】–>【项目】–>【Visual C++】–>【空项目】–>【修改工程位置和名称】–>【确定】.
- 将前面说的python的环境拷贝到该工程下。我的虚拟环境命名为 Pyhton_env。
- 在工程属性中配置。我的工程名为 Project1。
C/C++: $(SolutionDir)Project1\Python_env\include
链接器: $(SolutionDir)\Project1\Python_env\libs\python36.lib
dll: 将 Python_env/python36.dll 拷贝到工程路径下。2 代码编写
2.1 方式一
2.1.1 python脚本的编写
一定要确保,python脚本能够正确的运行通 !!!
class Student():
def __init__(self):
print("初始化Student成功============================")
def SetName(self, name="test1"):
print("进入Student.SetName成功=======================")
print("参数:name = " + name)
self._name = name
print("成功完成操作:self._name = " + self._name)
def PrintName(self):
print("进入Student.PrintName成功=====================")
print("self._name = " + self._name)
def hello():
print("进入 hello 函数成功=====================")
print("Hello World")
def world(name):
print("进入 world 函数成功=====================")
print("name")
2.1.2 C++脚本的编写
【类的实例化】
- 1 打开 python脚本 :
pModule = PyImport_ImportModule("test1013")
- 2 获取 模块属性字典: 也就是将python脚本中的定义的类、函数等存入到pDict
pDict = PyModule_GetDict(pModule);
- 3 根据类名 获取类:
pClass = PyDict_GetItemString(pDict, "Student");
- 4 获取 类的构造函数:
pConstruct = PyInstanceMethod_New(pClass);
- 5 类的实例
pInstance = PyObject_CallObject(pConstruct, NULL);
【类的调用】
PyObject_CallMethod(pInstance, "SetName", "s", "1111");
#include <Python.h>
#include <iostream>
#include <string>
#include <windows.h>
int main() {
// 添加的python的整个完成的环境包的路劲,必须添加。
Py_SetPythonHome(L"Python_env");
// 进行初始化
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 * pDict = NULL;
PyObject * pFunc = NULL;
PyObject * pClass = NULL;
PyObject * pConstruct = NULL;
PyObject * pInstance = NULL;
//这里是要调用的文件名
pModule = PyImport_ImportModule("test1013");
//加载文件中的函数名、类名
pDict = PyModule_GetDict(pModule);
if (!pDict)
{
printf("Cant find dictionary./n");
}
// 调用类==============================
{
// 根据类名获取该类
pClass = PyDict_GetItemString(pDict, "Student");
if (!pClass) {
printf("Can't find Student class.\n");
return -1;
}
// 得到类的构造函数
pConstruct = PyInstanceMethod_New(pClass); //python3的
if (!pConstruct) {
printf("Can't create Student instance.\n");
return -1;
}
// 类的实例化
pInstance = PyObject_CallObject(pConstruct, NULL);
PyObject_CallMethod(pInstance, "SetName", "s", "1111");
PyObject_CallMethod(pInstance, "PrintName", NULL, NULL);
}
//函数 并执行 ==================================
{
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);
}
//调用Py_Finalize,这个根Py_Initialize相对应的。
Py_Finalize();
return 0;
}
运行结果为:
注意:
PyObject_CallObject
的使用,在针对类、函数是不同的用法
- 类中使用:是对类进行实例化,并不是运行类的函数
- 函数中使用:赋予入参,运行函数
2.1.3 传参的方式
在最后补充
2.2 方式二
2.2.1 调用方式
方式一中调用python里函数,如果python工程较为复杂,就会在python工程中调用其他的python脚本时,出现问题(如找不到、报错、不运行等)。
推荐:如果调用的Python工程,可以一次性全部跑完,就可以用system("python ...")
的方式调用,会更加便捷。
import argparseparser = argparse.ArgumentParser(description='manual to this script')
parser.add_argument("--function", type=str, default="0")
parser.add_argument("--name", type=str, default="0")
args = parser.parse_args()
def hellow():
print("进入 hello 函数成功=====================")
print("Hello World")
def world(name):
print("进入 world 函数成功=====================")
print("name")
if (args.function=="hellow"):
hellow()
else:
world(args.name)
#include <Python.h>#include <iostream>
#include <string>
#include <windows.h>
int main() {
// 添加的python的整个完成的环境包的路劲,必须添加。
Py_SetPythonHome(L"Python_env");
// 进行初始化
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");
system("python test1013.py --function==world --name==1111");
system("python test1013.py --function==hellow");
Py_Finalize();
return 0;
}
2.2.2 可能出现问题
当在VS中可正常运行工程,当把该工程打包好,被其他的工程再次进行调用发现,找不到深度学习环境,尝试了很多方法后发现:去除系统环境变量中的python环境,然后将该工程要使用的环境进行系统环境变量设置即可。(也许会有更优的方式,有网友清楚望告知)
补充
从 C++ 向 python 传参之前,需要做类型转换。转为 PyObject* 才能传入 python。
- 例如:
C++的 int 是一个整数,该值占用 8bytes(64位)的存储空间,而一个 python 的 int 实际是一个 PyObject* 指向 24bytes。
前 8个bytes是整数,代表引用次数;中间 8bytes是指向 int 类型定义的指针,最后 8bytes是才是这个 int 的值。
所以 C++ 和 Python 之间参数互相传递都需要 Python提供的 api。
假设函数
func
的输入变量有三个,分别为 一个整数(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");
......
PyObject *pFunc = PyObject_GetAttrString(pModule, "func");
PyEval_CallObject(pFunc, args );
Py_DECREF(pFunc);
等价于
......
PyObject *pFunc = PyObject_GetAttrString(pModule, "func");
PyObject_CallFunction(pFunc, "ifs", 100, 3.14, "hellow");
Py_DECREF(pFunc);