背景
实际项目中我用的都是C++语言,因为涉及到高性能计算,所以其他听起来牛逼的语言只能用来打下手。现在遇到的一个项目,其深度学习模块是用python语言编写的,现在很多深度学习库都提供了python支持,但是很少有C++支持,就算有C++支持,在windows上编译也有一大堆问题(你说普通人会用Linux?)。幸好python是所谓的胶水语言,能够和C++兼容工作,所以就着手试验C++调用Python。
配置环境
我使用的配置环境是Windows10, VS2013,anaconda python3.6.4,tensorflow-gpu 1.7,cuda 9.0。
VS2013项目配置:
- 首先是项目属性-C++-include,要包含python.h这个头文件所在的目录,以及numpy/arrayobject.h 头文件所在的目录。后者是使用numpy的条件。
- 项目属性-链接器,附加库目录加入Miniconda3\libs,Miniconda3\Lib,还有numpy的core/lib目录。
- 项目属性-链接器,附加依赖项加入python36_d.lib和npymath.lib。如果没有_d的文件,就拷贝python36.lib并改名为python36_d.lib
debug模式下编译会报错,release不会,这是因为python头文件里面要检查lib文件的调试信息,然而我们只是吧release的lib文件强行当作debug的 lib文件使用。这里要修改头文件:
1 注释掉object.h第56行
//#define Py_TRACE_REFS
2 pyconfig.h 375行
//# define Py_DEBUG
然后debug就能编译了,但是我们看不到调试信息,只能依赖PyErr_Print()函数输出python错误信息了。
代码
下面是修改网上C++调用python2.7教程后的代码
#include <iostream>
#include <Python.h>
#include <numpy/arrayobject.h>
using std::cin;
using std::cout;
using std::endl;
using std::string;
int main(int argc, char *argv[])
{
Py_Initialize(); //初始化 python
import_array();
if (!Py_IsInitialized())
{
cout << "initialized error" << endl;
return -1;
}
PyObject* sys = PyImport_ImportModule("sys");
std::cout << std::string(Py_GetVersion()) << "\n";
PyRun_SimpleString("import sys"); // 执行 python 中的短语句
PyRun_SimpleString("print('come in python')");
PyRun_SimpleString("sys.path.append('./')");
PyObject *pModule(0), *pDct(0);
pModule = PyImport_ImportModule("hehe");
if (!pModule)
{
PyErr_Print();
cout << "can not find pytest.py" << endl;
return -1;
}
else
cout << "open Module" << endl;
double *CArrays = new double[3*4*5];
for (int i = 0; i < 60; ++i)
{
CArrays[i] = i + 1;
}
npy_intp Dims[3] = { 3, 4,5 };
PyObject *pDict = PyModule_GetDict(pModule);
PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_DOUBLE, CArrays);//构造numpy 数组
PyObject *ArgArray = PyTuple_New(1);
PyTuple_SetItem(ArgArray, 0, PyArray);
PyObject *pFunc = PyDict_GetItemString(pDict, "Show");//获取函数对象
PyObject_CallObject(pFunc, ArgArray);
PyErr_Print();
//Py_DECREF(PyArray);//注意!
Py_DECREF(ArgArray);
Py_DECREF(pDict);
Py_DECREF(sys);
Py_DECREF(pModule);
Py_Finalize();
system("pause");
return 0;
}
import numpy as np
def Show(Array):
print("Shape Of Array:", Array.shape)
print(Array)
最后输出:
网上其他教程CArray使用的是二维数组,我发现直接使用一维数组是可以的。此外,python2.7版本的教程往往把PyDict_GetItemString写成PyDict_GetAttrString函数,然而python3.6 是不能使用的,返回的总是一个null值,注意这里的区别。
Py_DECREF(PyArray);和Py_DECREF(ArgArray);只能保留一个,另外一个需要注释掉,因为PyArray和ArgArray被绑定在一起,清除ArgArray连带PyArray也会被清除。如果重复删除PyArray和ArgArray,那么程序执行到Py_Finalize() 就会报错0x0005c。