最近项目中需要将Python文件转化成C/C++可调用的文件,这个问题本来有两条思路可选:
1.用C/C++将Python文件重新写一遍
2.将Python文件封装成dll等可供C/C++调用的文件
因为.py代码较长,且里面调用了许多Python中的库函数,所以实现思路1实现起来相对困难,所以决定选择思路2.
对于思路2这里也有两种方案可供选择:
1.直接写C调用
2.借助Cython库完成对Python文件的封装
因为之前在用Cython时用到的坑实在太多,所以这次打算尝试一下使用方案1解决,即直接在C语言中调用Python函数。
一、入门尝试
参照博客C/C++调用Python进行入门尝试,需要注意的是头文件中include Python.h时是Python.h所在的文件路径,原博客调用的是Python2,所以这里也进行了相应的改动
/*直接include<Python.h>会报错,这里需要查看自己电脑上的Python.h所在的文件夹*/
#include <D:\Program Files\Python36\include\Python.h>
int main(int argc, char *argv[])
{
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
Py_SetProgramName(program);
Py_Initialize();
PyRun_SimpleString("print('Hello Python!'\n)");
Py_Finalize();
system("pause");
return 0;
}
运行结果如下:
不进行配置时,该代码会报 LINK : fatal error LNK1104: 无法打开文件“python36.lib” 的错误,解决该问题需要对VS进行一些配置
1. 配置库目录
在电脑中找到python36.lib所在的路径,D:\Program Files\Python36\libs\python36.lib,将该路径添加到项目属性页中>>VC++目录>>库目录中添加上述路径
2. 配置附加依赖项
在项目属性页中>>链接器>>输入>>附加依赖项中加入D:\Program Files\Python36\libs\python36.lib
3. 若配置好库目录和附加依赖项仍然报LNK1104无法打开文件“python36.lib”的错误
请检查你的python版本是32位还是64位,在相应的配置管理器中选择是Win32还是x64
4. 因为我这里用的是Release,所以是python36.lib,若是Debug,则对应的改为python36_d.lib,这里的36对应的是Python的版本号,若是其它版本进行相应的更改
5. 运行过程中遇到的其它错误
1. error C2664: ‘Py_SetProgramName’ : cannot convert parameter 1 from 'char ’ to 'wchar_t ’
解决方案:
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
Py_SetProgramName(program);
2. 未定义标识符 PyString_FromString, PyInt_AsLong
这些错误都是由于Python的版本不同引起的,“PyString_FromString”和“PyInt_AsLong”均是Python2中的,Python3中对应的是PyUnicode_FromString和PyLong_AsLong。
二、配置验证
若上述程序能正常运行说明基本的配置没有问题,接着尝试调用一个Python文件,新建了一个下面的程序,但在程序运行的时候竟然报了找不到去python36.lib文件的错误,这是因为上述过程配置的是在上述工程项目上的属性,这里新建了一个便又遇到了这个错误,但每次都重新配置一遍太过麻烦,这里查阅资料完成了一次配置,永久可用。
方法是在你新建的项目中找到视图>>其他窗口>>属性管理器,如下图所示
然后再看页面右侧是属性管理器,根据你用的是Release版本还是Debug版本已经是32位还是62位选择相应的文件夹下的Microsoft.Cpp.x64.user,双击会弹出属性页窗口
属性页窗口如下图所示,然后按照一中提到的方法配置库目录和附加依赖项即可。
在一个cpp文件中放入如下代码
#include <D:\Program Files\Python36\include\Python.h>
int great_function_from_python(int a) {
int res;
PyObject *pModule, *pFunc;
PyObject *pArgs, *pValue;
/*导入great_module模块,注意要将great_module.py放入工程文件夹中的x64文件夹下的Release文件夹下*/
pModule = PyImport_ImportModule("great_module");
/*在great_module.py中有一个great_function函数*/
pFunc = PyObject_GetAttrString(pModule, "great_function");
/*创建一个python类型的变量,这里均可以创建成tuple数据类型*/
pArgs = PyTuple_New(1);
/*给创建的变量赋值,中间的0表示的是tuple的第0项,Py_BuildValue("i", a)中的“i”表示的是int,还可以换成"d"Double,"s"string*/
PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", a));
/*
pArgs = PyTuple_New(3);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", a));
PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", a));
PyTuple_SetItem(pArgs, 2, Py_BuildValue("i", a));
*/
/*传入函数所需参数pArgs,如不需传入参数则位NULL,调用函数pFunc,pValue为返回值*/
pValue = PyObject_CallObject(pFunc, pArgs);
/*将返回值传递给C中的变量res*/
res = PyLong_AsLong(pValue);
return res;
}
int main(int argc, char *argv[]) {
Py_Initialize();
printf("%d", great_function_from_python(10));
Py_Finalize;
system("pause");
return 0;
}
其中great_module.py中代码如下,注意要将great_module.py放入到工程文件夹下的x64>>Release文件夹中,如下图所示。
def great_function(a):
return a + 1
若不将great_module.py放入正确的位置,会报错误引发了异常: 读取访问权限冲突。 v 是 nullptr。 出现了… 如下图所示
运行程序会得到如下结果,这样便可能调用一个Python文件
后来有尝试了更复杂的一些Python文件,甚至在一个py文件中import了另外一个py文件,均能返回正常的结果,import许多库包如itk,cv2,PIL,skelarn等均能正常运行。
另外加入了自己写的一个库函数也能正常调用,所以前期配置好了还是很方便将py文件在C/C++中调用的。
参考链接:
C/C++ 和 Python混合编程
混合编程之——C++调用python2.7&python3.5
第十五章:C语言扩展
C/C++ 调用Python