C++调用python脚本

C++调用python的脚本

一. 为什么?

缘由:用python写了机器学习的模型,项目工程代码是C++写得,所以在调用时,想通过C++调用python脚本,用c++获取返回值。(然后就是一个接着一个的坑。。。。)

二. 环境

环境win10 64 + VS2008 debug win32平台 + python3.5 32位 (anaconda64位里面安装的,或者直接下载32位python3.5)

注意:32bit的python与win32的VS平台是必须对应的!!!!!!或者全为32或者全为64。建议使用32

  1. 在VS中新建一个win32的项目,名字叫CPython。

  2. 打开VS2008, 加入python的环境 具体操作包括,加入C与pyhton的交互引用包。一个是include ,一个libs。
    在这里插入图片描述
    在这里插入图片描述

  3. 找到你python 环境目录下的 D:\Anaconda3\envs\python35_32\include\pyconfig.h 文件 (很重要,如果运行下面文件报错的话,请参考后面的错误解决办法)

将 pragma comment(lib,"python35_d.lib")  修改为:  pragma comment(lib,"python35.lib")#  define Py_DEBUG 注释掉              修改为:  //#  define Py_DEBUG
  1. 编写测试用例 在CPython.cpp中调用python脚本文件,TestC.py,其中包含了一个Hello函数和一个test_add(测试传递参数与返回参数)函数 c++中 CPython项目下的CPython.cpp文件

    #include "stdafx.h"
    #include <Python.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <iostream>
    using namespace std;
    
    int main()
    {       //指定python.exe位置  python的环境。后来在迁移环境时,将include libs放在工程目录下,指定工程目录就可以
            Py_SetPythonHome(L"D:\\anaconda3\\envs\\python3");//指定python.exe位置需要修改成自己的  python的环境
            Py_Initialize();
            // 将当前目录加入sys.path  这两行的意思是你的主目录位置:是在工程目录下因此要把.py文件放到该目录下!。也可以自己定义
            PyRun_SimpleString("import sys");
            PyRun_SimpleString("sys.path.append('./')");
            // 导入hello.py模块
            PyObject *pmodule = PyImport_ImportModule("TestC");
            if (!pmodule)
            {
                   printf("can't find pytest.py");
                   return 1;
            }
            {
                   PyObject* pFunc = PyObject_GetAttrString(pmodule, "Hello");
                   PyEval_CallObject(pFunc, NULL);
                   Py_DECREF(pFunc);
            }
            {
                   PyObject* pv = PyObject_GetAttrString(pmodule, "test_add");
                   if (!pv || !PyCallable_Check(pv))
                   {
                           cout << "Can't find funftion (test_add)" << endl;
                           return 0;
                   }
                           cout << "Get function (test_add) succeed." << endl;
                   //初始化要传入的参数,args配置成传入两个参数的模式
                   PyObject* args = PyTuple_New(2);
                   //将Long型数据转换成Python可接收的类型
                   PyObject* arg1 = PyLong_FromLong(4);
                   PyObject* arg2 = PyLong_FromLong(3);
                   //将arg1配置为arg带入的第一个参数
                   PyTuple_SetItem(args, 0, arg1);
                   //将arg1配置为arg带入的第二个参数
                   PyTuple_SetItem(args, 1, arg2);
                   //传入参数调用函数,并获取返回值
                   PyObject* pRet = PyObject_CallObject(pv, args);
                   if (pRet)
                   {
                           //将返回值转换成long型
                           long result = PyLong_AsLong(pRet);
                           //cout << "result:" << result << endl ;
                   }
            }
      
            Py_DECREF(pmodule);
    
            Py_Finalize();
            return 0;
    }
    

    TestC.py文件 的内容 注意文件的空格与tab键。可以先将在pyhton环境下运行下,看是否出错。脚本文件一定不能出错!!!

    def Hello():
        print ('Hello')
        
    def test_add(a, b):
        print ('a+b')
        return a+b
    

    测试成功后,显示:

在这里插入图片描述

  1. 到此为止,你的环境算是布置成功了 (只是成功了一小步,╮(╯▽╰)╭)有问题的参考后面的解决方案!!!!!

  2. 下面你可能想要向python 传递列表、数组,并且以列表、数组的形式返回给C++调用。不要着急!!!!

    在python与c++的交互过程中,可以参考这里

三. 配置时遇到的问题

  1. 问题:

    error LNK2001: 无法解析的外部符号 __imp_PyString_FromString
    error LNK2001: 无法解析的外部符号 __imp_PyCallable_Check
    error LNK2001: 无法解析的外部符号 __imp_PyObject_CallObject
    error LNK2001: 无法解析的外部符号 __imp_Py_Initialize
    error LNK2001: 无法解析的外部符号 __imp_Py_IsInitialized
    error LNK2001: 无法解析的外部符号 __imp_PyImport_Import
    error LNK2001: 无法解析的外部符号 __imp_PyModule_GetDict

    解决办法: 开发环境 win7 x64 vs2008 64 python3.5 32位 出现上述问题是因为 python与 vs的开发环境位数不一致导致。更改py或 者vs平台一致即可

  2. 问题:

    1>CPython.obj : error LNK2019: 无法解析的外部符号__imp___Py_NegativeRefcount,该符号在函数_wmain 中被引用
    1>CPython.obj : error LNK2001: 无法解析的外部符号__imp___Py_RefTotal

    解决办法: 修改配置文件 python目录\include\pyconfig.h 一定要!!! 注释掉 //# define Py_DEBUG

  3. 问题:

    fatal error C1083: 无法打开包括文件:“inttypes.h”: No such file or directory,缺什么,去下载什么。下载文件安装到VC的目录下,再次执行,直接命令框死掉xxx00000。

    解决办法:个人感觉是版本原因,可能你的计算机缺什么文件。建议重新安装其他本版本最好都是32位的,纯净的python+vs。甚至系统

  4. 问题:

    无法启动此程序,因为计算机中丢失python35.dll。 尝试重新安装该程序以解决此问题。

    解决方案:去python目录下找到python35.dll,复制到C:\Windows\SysWOW64 中,也可以尝试放在c++工程目录下

  5. 问题

    python35_d.dll 出错

    解决方案: 找到你python 环境目录下的 D:\Anaconda3\envs\python35_32\include\pyconfig.h 文件 (很重要,如果运行下面文件报错的话,请参考后面的错误解决办法

    将 pragma comment(lib,“python35_d.lib”) 修改为: pragma comment(lib,“python35.lib”)

    将 # define Py_DEBUG 注释掉 //# define Py_DEBUG

  6. 问题:

    运行程序,出现no moudle…问题。

    解决方案:是环境问题,在cpython.cpp中加入 Py_SetPythonHome(L"D:\anaconda3\envs\python3");//指定python.exe位置(这里指定的conda中的一个32位python的虚拟环境)

  7. 问题:

    [ERROR] Python get module failed. 在加载模块时, PyObject *pmodule = PyImport_ImportModule(“TestC”);
    解决办法: py文件中错误,查看是否有空格与tab键不一致,是否有加载其他的外部包,尝试去掉所有的import。

  8. 问题: PyInt_AsLong PyString_FromString 这些函数报错。

    解决办法:py3 用PyBytes_FromString 代替PyString_FromString PyLong_AsLong代替PyInt_AsLong。其他的可以去搜一下具体用法。

  9. 问题: nullptr 报错 error C2065: “nullptr”: 未声明的标识符
    解决办法: C++11好像才有,现在 报错 直接为空就好了NULL

  10. 问题:from _ctypes import Union, Structure, Array DLLload failed找不到指定的程序

    或者multiarray 导入错误什么的,ImportError: numpy.core.multiarray failed to import,

    原始是在c++程序中有一句import_array(); 找不到合适的numpy版本

    解决办法:确认python的版本位数numpy的版本位数都是32位,并在工程中导入numpy的地址,如下:

在这里插入图片描述

四. c++与python数据交互案例(完整的工程代码)

除了上面举得安装环境时的测试例子,还有一种交互方式,比较方便通用。下面展示的是在c++中调用python写的BP神经网络函数,并传入参数,最后在c++中接收返回结果。

c++传递数据到python中:

//初始化
Py_SetPythonHome(L"D:\\anaconda3\\envs\\python3");//指定python.exe位置
Py_Initialize();   //初始化

PyRun_SimpleString("print ('Hello')");
import_array();

//指定脚本文件TestC的位置,在该cpp工程文件目录下
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
PyObject *pmodule = PyImport_ImportModule("TestC");

//调用指定模块
PyObject* pFunc = PyObject_GetAttrString(pmodule, "BP_Model");

//data_file, test_size=0.3, hidden_layer_sizes=(100,), activation='relu', learning_rate_init=0.0005,  max_iter=1000, tol=1e-5
//传递参数 字符串、整形、double类型
char * cFilePath = "data\\hoston_DataImport_Temp.csv";
PyObject* args = PyTuple_New(7);
PyObject* arg0 = Py_BuildValue("s", cFilePath); // 文件路径
PyObject* arg1 = Py_BuildValue("d",0.3);		// 验证集比例
PyObject* arg2 = Py_BuildValue("s","100,100"); // 隐藏层节点数
PyObject* arg3 = Py_BuildValue("s", "relu"); // 激活函数
PyObject* arg4 = Py_BuildValue("d", 0.005); // 学习率
PyObject* arg5 = Py_BuildValue("i", 10000000); // 迭代次数	
PyObject* arg6 = Py_BuildValue("d",0.0000000000001); // 迭代停止条件误差小于1e-5

PyTuple_SetItem(args, 0, arg0);
PyTuple_SetItem(args, 1, arg1);
PyTuple_SetItem(args, 2, arg2);
PyTuple_SetItem(args, 3, arg3);
PyTuple_SetItem(args, 4, arg4);
PyTuple_SetItem(args, 5, arg5);
PyTuple_SetItem(args, 6, arg6);
//返回调用结果
PyObject* pyResult = PyObject_CallObject(pFunc, args);

//解析返回结果,含有多个字符串结果。
char *ret1 ;
char *ret2;
PyArg_ParseTuple(pyResult,"ss", &ret1,&ret2);
Py_DECREF(pyResult);
printf("\nBPreturn value: 模型:%s, 误差:%s", ret1,ret2);
Py_DECREF(pFunc);

TestC.py文件在当前工程目录下:

#coding = utf-8
'''
=============================================
    File Name:     TestC1
    Email:         xxxx
    Author :       zzl
    Date:         2020/06/15
    Description :
=============================================
'''
def BP_Model(data_file, test_size=0.3, hidden_layer_sizes='100', activation='relu', learning_rate_init=0.0005,
             max_iter=1000, tol=1e-5):
    '''
    BP神经网络模型训练
    Args:
        data_file: 传入的文件地址
        test_size: 验证集比例
        hidden_layer_sizes: 隐藏层节点数(字符串),后期对字符串进行处理转化成tuple
        activation: 激活函数
        learning_rate_init: 学习率
        max_iter: 最大迭代次数
        tol: 模型停止条件
    Return:
        str: 训练后的模型地址
        error: 验证集误差
    '''
	数据读取
	模型训练
	返回结果
    return s_, error

详细代码

五. c++与python模块间的相互调用

上面所使用的过程都是用c++想要调用python的函数,这样做的目的是,python对分析、应用、算法的拓展上比较方便,而c++在运行速度和工程化上有着无可比拟的优势(python是拿c写的,想像一下,我用python写了一个算法,然后内部是调用c的函数,然后再转化成汇编,跟c直接转化成汇编,哪个快?)。除了上面的一种数据交互方式,boost的python也可以进行交互C++和Python混合编程第五期:C++调用Python脚本的简单方法

在实际使用中,还有一种应用方式,就是用c++来写底层算法,编译成python可以用的模块,在使用时直接调用这些模块,大大加快的运算速度,有很多的深度学习框架底层都是c++实现的,然后给python留了接口,模块。在实际的应用中,还是这种应用方式比较普遍。

另外在电脑上将cpp打包dll时,将项目 配置属性->常规->配置类型->动态库 即可在win10上打包dll库。

下面举一个例子来说明python调用C++实例:用C++对numpy执行BFS(广度优先搜索)

用python实现BFS: 最终程序运行0.914s

用C++实现BFS: 形成一个cpp文件, 下面我们看怎样用python调用cpp。

  1. 在上文的cpp中,对想要执行的函数fillHole先进行声明:
  2. 用g++(mingw64位)编译为dll:
  3. 在python中使用numpy的封装加载DLL并且传参调用:

程序执行了0.058秒,根据测试cpp比python快了15倍。

六. 引用

  1. 多维数组Numpy.Array()在Python和C/C++文件间的传递问题 https://blog.csdn.net/stu_csdn/article/details/69488385
  2. c++接受python的array返回值 https://docs.scipy.org/doc/numpy/reference/c-api.array.html
  3. C++使用Py*调用Python3模块中 https://www.jianshu.com/p/c9f5f4ce3e7a?utm_campaign=maleskine
  4. VS2019 C++ 调用python函数/类对象的方法 https://blog.csdn.net/omg_orange/article/details/100106926
  5. C++调用python的那些坑(详细教程 https://blog.csdn.net/qq_38275373/article/details/91367372
  6. C++调用Python浅析 https://blog.csdn.net/magictong/article/details/8947892
  7. 浅析C++调用Python模块 https://blog.csdn.net/tobacco5648/article/details/50890106
  8. 在其他应用程序中嵌入 https://docs.python.org/2/extending/embedding.html
  9. C/C++调用Python OpenCV与Numpy https://blog.csdn.net/ziweipolaris/article/details/83689597
  10. python与CC++的交互(一) https://blog.csdn.net/guxch/article/details/80332821
  • 9
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值