最近碰到了一个奇怪的需求,需要用C++调用Python脚本(Python调用C++倒是常见,反过来以前真没见过),因此去网上学习了一波顺便将学习过程中的笔记分享一波,话不多说,请看下文。
二.VS中调用Python配置首先在Visual Studio中创建项目,注意需要选择X64进行编译,即:
然后,需要将Python安装目录下的include和libs目录引入到本项目中去,具体做法如下:
- 对于include,选择项目–>配置属性–>C/C+±->附加包目录,在附加目录那一栏填入include的绝对路径。
- 对于libs,选择项目–>配置属性–>链接器–>附加库目录,在附加库目录那一栏填入libs的绝对路径,注意还需要将pythonxx.lib复制一份为pythonxx_d.lib仍然放在该文件中。
xx表示python的版本,比如3.8版本即为python38
完成上述步骤就可以在VS的cpp源文件中引入头文件Python.h头文件,并使用Python提供的C API库了。
三.C++中Python的使用方法 3.1 Python解释器环境首先需要为程序提供解释器环境,在程序结束后要销毁创建的解释器,与解释器相关的接口如下:
// 设置python环境EXE文件的路径 void Py_SetPythonHome(python_path); // 初始化Python解释器 void Py_Initialize(); // 解释器是否初始化完成,失败则返回0 int Py_IsInitialized(); // 销毁创建的解释器 void Py_Finalize();3.2 调用Python模块和调用相关的函数
这里指的Python模块包括封装好的库以及自定义的Py文件。首先给出最简单的调用方法(无法传递参数),其对应的接口如下所示:
// 直接以字符串的形式传入python代码 int PyRun_SimpleString(const char*); // PyRun_SimpleString("print('hello world')"); // 将python脚本传入直接执行,fp为脚本对应的文件指针,filename为脚本名 int PyRun_SimpleFile(FILE *fp, const char *filename);
倘若要调用自定义的python脚本,则可以使用下面的常用接口:
// 加载模块,传入python脚本名或封装好的库名 PyObject* Pyimport_importModule(char *name); // 获取模块中的函数列表,该函数返回一个key为函数名,value为函数调用地址的字典 PyObject* PyModule_GetDict( PyObject *module); // 从函数字典中获取相应的函数,dp为函数字典,key为要获取函数的名称 PyDict_GetItemString(PyObject *dp, const char *key); // 直接传递属性名来获取模块中的相应对象 PyObject* PyObject_GetAttrString(PyObject *o, char *attr_name); // 函数调用, callable_object为函数对象,args为需要传入的参数 PyObject* PyObject_CallObject( PyObject *callable_object, PyObject *args); // 参数传递 // 将C++的数据类型转换为python对应的类型 PyObject* Py_BuildValue( const char *format, ...); // 定义size为len的元组 PyObject* PyTuple_New( Py_ssize_t len); // 创建完元组后,往元组内添加元素,p为要 *** 作的元组,pos为索引,o为通过Py_BudilValue转换后的值 int PyTuple_SetItem( PyObject *p, Py_ssize_t pos, PyObject *o) ;
3.3 C++与Py的数据类型转换注意函数的参数列表必须为元组。
上节介绍到通过Py_BuildValue函数可以进行数据类型的转换,下面给出常用数据类型对应的转换关系表:
3.4 返回值的处理对于C++传递数组到Python限于篇幅,本文就不展开了,具体可以参考下C调用Python(传递数字、字符串、list数组(一维、二维),结构体)
python脚本的返回值可以用下面的API进行处理:
int PyArg_Parse( PyObject *args, char *format, ...); // PyObject* pReturn = Pyeval_CallObject(pFunc, pArgs); // int nResult; // PyArg_Parse(pReturn, "i", &nResult);
四.使用示例需要注意若返回的是多个参数,可以用处理列表、元组或字典的方法来进行解析。
首先创建一个名为temp的py文件,其中包含的源码如下:
def hello(name): print("hello {}".format(name)) def add(a,b): return a + b
然后将该文件放至VS项目可执行文件同目录下,具体做法为:
- 若使用Debug(x64)模式,则在项目目录–>x64–>Debug目录下
- 若使用Release(X64)模式,则在项目目录–>x64–>Release目录下
或者也可以利用PyRun_SimpleString函数来调用sys模块来进行指定如:
PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append('C:/xxxxx')"); // c:/xxxxx表示py文件所在的位置
然后,便是c++程序的编写,对应的源码如下:
#include#include using namespace std; int main() { // 设置python的执行路径 Py_SetPythonHome(L"C:/xxxxx"); Py_Initialize(); if (Py_IsInitialized()) { PyObject* pModule = NULL; PyObject* pFunc = NULL; // 直接指定.py文件的位置 // PyRun_SimpleString("import sys"); // PyRun_SimpleString("sys.path.append('C:/xxxxx')"); pModule = Pyimport_importModule("temp"); if (pModule) { pFunc = PyObject_GetAttrString(pModule, "add"); PyObject* pArgs = PyTuple_New(2); PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 2)); PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 3)); PyObject* pReturn = Pyeval_CallObject(pFunc, pArgs); int nResult; PyArg_Parse(pReturn, "i", &nResult); cout << nResult << endl; pFunc = PyObject_GetAttrString(pModule,"hello"); PyObject* pArgs1 = PyTuple_New(1); PyTuple_SetItem(pArgs1,0, Py_BuildValue("s", "XiaoMing")); PyObject_CallObject(pFunc, pArgs1); } } Py_Finalize(); return 0; }
执行以上程序就可以输出如下的结果:
五.结语博主在学习的过程中参考了如下大佬的资料:
Visual Studio中C++的包含目录、附加包含目录和库目录和附加库目录的区别
C++ 调用Python脚本
Visual Studio 2019 添加dll库
C++调用python方法及环境配置(Windows环境、VS工具)
以上便是本文的全部内容,要是觉得不错的话就点个赞或关注一下博主吧,你们的支持是博主继续创作的不解动力,当然若是有任何问题也敬请批评指正!!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)