2018-11-25随笔-今天谈谈C++嵌入Python脚本中遇到的问题
Posted ddl-2018
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018-11-25随笔-今天谈谈C++嵌入Python脚本中遇到的问题相关的知识,希望对你有一定的参考价值。
由于现在很多底层协议用C/C++,然后机器学习或者深度学习等算法模型使用基于Python的TensorFlow来实现。所以现在C++用来做框架,做软件界面,然后调用Python的算法脚本来进行计算是很常见的需求。
我们的项目中也存在着这样的需求。下面来记录一下相应的实现方式。
背景:C++上用MFC做界面,Python上是import了numpy与pandas模块的处理功能(后续的基于sklearn与TensorFlow的脚本还没嵌入,配置方式相同,后续实验会再放上来)
软件配置:win10/64位+VS2013+Python3.6/64位
- 在Python的安装目录下,找到libs文件夹,将其中的python36.lib复制一份并命名python36_d.lib,功能是用于VS的调试
- 在VS中新建项目(随便什么项目,我使用的是控制台程序),在项目-项目属性中设置活动平台为X64,目的是匹配Python的版本
- 在VS的项目属性里面:C++->常规->附加包含目录,输入..include
- 链接器->常规->附加目录项,输入..libs;链接器->输入->附加依赖项,添加python36.lib;python36_d.lib;
- python36.dll拷贝到Debug目录下(注意是项目名->X64->debug文件夹里面,与***.exe同目录);将py文件拷贝到Debug目录下(同上)
我的py文件中,需要调用的是其中一个readcase函数,其中需要传入一个文件路径参数,再对文件里面的内容进行相应的处理。
C++代码(这里面我参考了网上的一些案例,上上了一些注释-_-||,需要注意的点我在后面再补充)
1 #include <iostream> 2 #include <Python.h> 3 using namespace std; 4 5 extern "C" 6 { 7 #include "Python.h" 8 } 9 //调用输出"Hello Python"函数 10 void Hello() //没有传参的话就没啥问题 11 { 12 Py_SetPythonHome(L"G:Python3.6.3_ 64"); 13 Py_Initialize();//调用Py_Initialize()进行初始化 14 PyObject * pModule = NULL; //声明在C++中的用到的Python变量 15 PyObject * pFunc = NULL; 16 pModule = PyImport_ImportModule("c_usepython");//调用的Python文件名,test_Ctopython 17 pFunc = PyObject_GetAttrString(pModule, "Hello");//调用的函数名 18 PyEval_CallObject(pFunc, NULL);//调用函数,NULL表示参数为空 19 Py_Finalize();//调用Py_Finalize,和Py_Initialize相对应的. 20 } 21 //调用Add函数,传两个int型参数 22 void Add() //测试传参的例子, 23 { 24 Py_SetPythonHome(L"G:Python3.6.3_ 64"); 25 Py_Initialize(); 26 PyObject * pModule = NULL; 27 PyObject * pFunc = NULL; 28 pModule = PyImport_ImportModule("c_usepython");//Python文件名 29 pFunc = PyObject_GetAttrString(pModule, "Add");//Add:Python文件中的函数名 30 //创建参数: 31 PyObject *pArgs = PyTuple_New(2);//函数调用的参数传递均是以元组的形式打包的,2表示参数个数 32 PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 6));//0--序号,i表示创建int型变量 33 PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 8));//1--序号 34 //返回值 35 PyObject *pReturn = NULL; 36 pReturn = PyEval_CallObject(pFunc, pArgs);//调用函数 37 //将返回值转换为int类型 38 int result; 39 PyArg_Parse(pReturn, "i", &result);//i表示转换成int型变量 40 cout << "6 + 8 = " << result << endl; 41 //Py_Finalize(); 42 } 43 void Readcase2() // 44 { / 45 char* picpath = "D:\\file111.dat";//需要读取的路径参数,这里的路径太长就一直无法正常运行,C++里面没有报错,但是调用老是失败,返回py里面的threading assert tlocked()错误,我觉得是因为参数没有传进去,Python才报错 46 cout<<picpath<<endl; 47 Py_SetPythonHome(L"G:Python3.6.3_ 64"); 48 Py_Initialize(); 49 //PyEval_InitThreads(); //初始化进程 50 PyObject * pModule = NULL; 51 PyObject * pFunc = NULL; 52 PyObject* pRetVal = NULL; 53 //PyObject * pArgs=NULL ; 54 pModule = PyImport_ImportModule("c_usepython");//Python文件名 55 pFunc = PyObject_GetAttrString(pModule, "readcase");//Python文件中的函数名 56 //创建参数: 57 PyObject *pArgs = PyTuple_New(1);//函数调用的参数传递均是以元组的形式打包的,2表示参数个数 58 PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", picpath));//0--序号,i表示创建int型变量 59 //pArgs = PyTuple_New(1); 60 //PyTuple_SetItem(pArgs,0,Py_BuildValue("s", "G:\\实验室工作\\CT项目软件工作\\75-1-dao"));//这个字符串太长了,导致调用失败 61 //PyObject *pReturn = NULL; //我这个函数不需要返回,具体工作都在Python里面做了,以后会返回数组来进行下一步工作 62 //PyGILState_STATE gstate; //这个是关于GIL的一些内容,具体功能不太了解,当初调试的时候失败,似乎没啥用 63 //gstate = PyGILState_Ensure(); 64 //PyEval_CallObject(pFunc, pArgs); 65 PyEval_CallObject(pFunc, pArgs);//调用函数,PyObject_CallObject()这个函数也可以,两者功能很相似 66 //PyGILState_Release(gstate); 67 //将返回值转换为int类型 68 //char result; 69 //PyArg_Parse(pReturn, "s", &result);//s表示转换成string型变量 70 //cout << "结果: " << result << endl; 71 Py_Finalize(); 72 } 73 int main(int argc, char** argv) 74 { 75 //cout << "调用Test001.py中的Hello函数..." << endl; 76 ////Hello(); 77 //cout << " 调用Test001.py中的Add函数..." << endl; 78 //Add(); 79 cout << " 尝试调用test_Ctopython中的readcase文件..." << endl; 80 Readcase2(); 81 system("pause"); 82 return 0; 83 }
结果能跑起来,暂时先这样吧,想到什么再说吧。后面的TensorFlow实验,有时间做了再上传。
以上是关于2018-11-25随笔-今天谈谈C++嵌入Python脚本中遇到的问题的主要内容,如果未能解决你的问题,请参考以下文章
周报Seele进展周报2018.11.19-2018.11.25