作者:非妃是公主
专栏:《笔记》《C++》 《python学习》
个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩
前言
前几天利用C++调用Python脚本,在进行语言融合的过程中,踩到了好多坑,在此总结一下!
C++执行python代码
- 首先我们需要配置C++调用python所需要的链接库,将include和libs文件夹放到工程文件目录下(和.sln文件放在一起)
- 两个文件在python的安装目录下,比如我的:
- 在项目中配置环境依赖
右键项目-》选择属性-》C/C+±》常规-》附加包含目录,添加include文件夹的相对路径(../include
),../
为返回上一级目录。(当前cpp的上一级目录就是include所在目录)
右键项目-》选择属性-》选择连接器-》常规-》附加库目录-》添加libs的相对目录../libs
,../
为返回上一级目录。(当前cpp的上一级目录就是libs所在目录)。
- 点击应用,然后关闭窗口,编译器切换到release,选择64位,这块要根据子集python的版本,因为我的是64位,就只能在MSVC64的环境下运行,而且我的python没有安装python在MSVC下的调试器,所以只能在release下运行(如果安装的时候没有特别注意,都是我这样安装的)。
- 运行下面的代码
#include <Python.h>
int main(int argc, char* argv[]) {
Py_Initialize();
PyRun_SimpleString("print('hello world')\n");
Py_Finalize();
return 0;
}
- 控制台弹出以下信息
这是在说明,我们没有设置PYTHONHOME
和PYTHONPATH
这两个环境变量。
新建PYTHONHOME
用户变量,变量值设置为Anaconda3所在的目录下即可(为了找到python.exe)
同样,新建PYTHONPATH
用户变量,变量值与PYTHONHOME
相同
这样用户变量就配置好了。
重新运行,发现还是报错(这是由于环境变量需要重启后才能生效,重启电脑)。
- 重启电脑后,发现运行正常了,打出了
Hello World
(这里注意一下,这个环境变量会对虚拟环境造成影响,如果用完之后要用其它虚拟环境,一定要注意删除这两个环境变量,否则会产生无法识别的错误)这篇文章中有记录不删除这两个环境变量会产生的问题
- 对以上代码进行简单的说明,如下图:
- 上面我们解决了如何利用C++执行python代码的问题,那么很多时候我们的python代码都是比较复杂的(包括分支循环,作用域等),用字符串表示起来是不方便的,是否可以存在一种可以直接调用py文件的方法呢?答案是肯定的。
C++调用.py文件
我们新建C++工程,然后按照上面进行目录的配置(libs和include),编译环境的切换(release,X64)。
然后新建.py文件,并放在工程文件名/X64/release
文件夹下,与工程的.exe文件放在一起,不然会找不到。
代码如下:
#include <Python.h>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
Py_Initialize();// Python 接口初始化
PyObject* pModule = NULL; // 模块句柄
PyObject* pFunc = NULL;// 函数句柄
PyObject* pName = NULL;//
pModule = PyImport_ImportModule("Test"); // 文件名
if (pModule == NULL) // 如果没找到返回
{
cout << "没找到" << endl;
return 0;
}
pFunc = PyObject_GetAttrString(pModule, "Fun"); // 函数名
PyObject* pArgs = PyTuple_New(2);// 函数调用的参数传递均是以元组的形式打包的,2表示参数个数
PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 222));// 0 表示第一个参数 i表示 int类型 222表示具体参数
PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 444));// 1 表示第二个参数 i表示 int类型 444表示具体参数
PyObject* pReturn = PyObject_CallObject(pFunc, pArgs);// 调用函数
int nResult;// 储存结果的C++类型
PyArg_Parse(pReturn, "i", &nResult);// i表示转换成int型变量
cout << nResult << endl; // 输出
Py_Finalize();// 结束Python接口初始化
return 0;
}
至于返回值的传递是一个比较麻烦的问题,由于这是一个老接口了,所以并不是很好用,在参数传递的过程中我踩了好多坑,最终一种比较好用的解决方法就是将返回值写进文件,然后在C++中写代码去读文件,当然这样效率会比较低,但是相信使用C++和python进行语言融合的项目都是想短期实现,对性能要求不高的。自己写的一个爬虫程序效果如下:
每运行一次OneNoteEveryDay.exe会生成一个OneNoteEveryDay.txt。
C++调用爬虫
爬虫代码如下,有兴趣的友友可以尝试一下,还挺好玩的!(^o^)!
import random
from bs4 import BeautifulSoup
import requests
import ctypes as ct
import re
def Fun():
res = requests.get('http://quotes.toscrape.com/')
res.raise_for_status()
html = res.text # 页面内容
soup = BeautifulSoup(html, 'lxml')
items = soup.find_all('span', class_='text')
words = []
for item in items:
words.append(item.text)
# f.write(output + '\n')
f = open("OneNoteEveryDay.txt", 'w')
number = random.randint(0, len(words) - 1)
words[number] = re.sub("“", "", words[number])
words[number] = re.sub("”", "", words[number])
f.write(words[number])
f.close()