@Coding Environment VS2019 Python3.7 Qt5.1
这是我第一次python c/c++接口尝试,面对网上资源不全,版本不一,官网API文档阅读困难,特写此篇,希望能给一些人避开那些恶心的坑.
文章目录
从一个简单的python爬虫开始
爬取有道词典的包,来翻译文件,代码如下:
// Python3.7 at Pycharm
#coding=utf-8
from urllib import request
from urllib import parse
import json
#这是一个接口函数,输入data来进行翻译,在此不再过多解释,返回值为有道的翻译内容
def fanyi_youdao_API_21(data,from_d = 'AUTO',to_d = 'auto'):
Request_URL="http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule"
form_data={}
form_data['i']=data
form_data['from'] = from_d
form_data['to'] = to_d
form_data['smartresult'] = 'dict'
form_data['doctype']='json'
form_data['version']='2.1'
form_data['keyfrom']='fanyi.web'
form_data['action']='FY_BY_CLICKBUTTION'
form_data['typoResult']='false'
data=parse.urlencode(form_data).encode('utf-8')
response=request.urlopen(Request_URL,data)
html=response.read().decode('utf-8')
translate_results = json.loads(html)
translate_result = translate_results["translateResult"][0][0]['tgt']
return translate_result
# c = fanyi_youdao_API_21('hello')
# print("翻译结果:%s" % c)
正头戏C++调用python接口(误)但被开篇劝退
好了正片来了,我们希望能用c/c++调用这个接口函数,来实现翻译的目的
于是乎我去翻了官网的API文档API
原来是这样
#define PY_SSIZE_T_CLEAN
#include <Python.h>
就可以了,但是vs里显然没有这样的库.翻了翻文件,在python的文件目录里找到了Python.h
简单包含目录嘛试着跑一下:不行python37_d.lib链接错误
找啊找啊找资料,终于找到一个靠谱的
修改完毕,什么还是没有lib?
这时要在配置里附加lib的位置
好的添加完成,终于可以了吧???
好的,报错了,再见
怎么可能这么放弃了呢?
在笔者找了好久后终于找到问题:64位系统安装64位python,必须在x64环境下编译
所以只要改成就可以了
在这里值得一说的是,改完环境前面的几步全!部!要!再!弄!一!次!
那些劝退的开头几坑总结
python37_d.lib链接错误
参考1
https://blog.youkuaiyun.com/weixin_40127330/article/details/99731879?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2
error LNK2001: 无法解析的外部符号 __imp_PyObject_GetAttrString 等一大串报错
把编程环境切换为64位的
真_正头戏C++调用python接口
先看一篇简单的代码
#include <stdio.h>
#include <Python.h>
#include <iostream>
using namespace std;
int main() {
// 初始化Python虚拟机
Py_Initialize();
// 判断Python虚拟机是否成功
if (Py_IsInitialized() == 0) {
cout<<"fal to initialize Python"<<endl;
return -1;
}
cout << "server start" << endl;
// 退出Python虚拟机
Py_Finalize();
cout << "server end" << endl;
return 0;
}
一切正常,我们在c\c++中已经可以调用python虚拟机了,也说明前面的坑顺利跨过.
python的数据:PyObject
PyObject是什么?就是存放python数据的类,每个指向Python对象的指针都可以转换为PyObject *.知道这些就可以了,我们是要看怎么用,而不是怎么实现,怎么实现和详细功能可以等基础了解清楚后再了解.
创建的方式很简单:
PyObject* a;
至于PyObject对象怎么创建python.h为我们提供了一种函数:
Py_BuildValue
他可以使用其将C的所有基本数据类型转换成Python可访问的数据类型。
类型互相转化:
(依旧用上一段代码,加了几行)
#include <stdio.h>
#include <Python.h>
#include <iostream>
using namespace std;
int main() {
// 初始化Python虚拟机
Py_Initialize();
// 判断Python虚拟机是否成功
if (Py_IsInitialized() == 0) {
cout<<"fal to initialize Python"<<endl;
return -1;
}
cout << "server start" << endl;
PyObject* p_val1 = Py_BuildValue("i", 233);
int c_val = PyLong_AsLong(p_val1);
// 退出Python虚拟机
cout << c_val << endl;
Py_Finalize();
cout << "server end" << endl;
return 0;
}
PyObject* p_val1 = Py_BuildValue("i", 233);//i表示int,233是(int)233,转化为PyObject
int c_val = PyLong_AsLong(p_val1);//将PyObject转化为int
这两行是两种类型的互相转化
同理long int 只要将i改为l,PyLong_AsLong改为PyLong_AsLongLong即可.如下表
类型 | 第一个参数 | 对应的转化 |
---|---|---|
int | Py_BuildValue(“i”, 233) | PyLong_AsLong(p_val1) |
unsign int | Py_BuildValue(“I”, 233)大写i | PyLong_AsUnsignedLong(p_val1) |
long long | Py_BuildValue(“l”, 233333333333)小写L | PyLong_AsLongLong(p_val1) |
float/double | Py_BuildValue(“d”, 233.3) | PyFloat_AsDouble(p_val1) |
bool | Py_BuildValue(“b”, true) | PyInt_AsLong(p_val1) |
对于的转化方式除了这种以外还可以用PyArg_Parse()来实现,但是要在堆空间里事先声明指针结构
不过string有些不同(尤其是python3):
网上资料十分散乱而且还都是python2的.我足足找了2小时资料,终于有了办法
虽然不是最好的,但是是可行的
const char* pstr = "this is a test";
PyObject* py_str = Py_BuildValue("s", pstr); // Python 字符串对象
PyObject* u_str = PyCodec_Encode(py_str, "utf-8",NULL);//Python unicode 对象
char* c_pstr = PyBytes_AsString(u_str); // 转成C的字符指针
printf("c_pstr = %s\n", c_pstr);
不过要说一点就是 这种代码不支持中文
这里是详细的中文参数的教程
调用函数
好了基础就是这些,下面我们开始真正的调用函数
函数的标准调用如下:
int x;//x为参数数目
PyObject* pArgs = PyTuple_New(x);
PyTuple_SetItem(pArgs, 0, y);参数为y,设置第一个参数 y为PyObject对象
...//这里是设置参数
PyObject* pReturn = PyEval_CallObject(pFunc, pArgs);//preturn为返回值
对于这种简单的函数
def callTest(string):
print('String is ->:' + string)
#返回结果
reStr = 'call Test Succeed'
return reStr
```
调用代码如下
```
#include <Python.h>
#include <iostream>
using namespace std;
int main() {
// 初始化Python虚拟机
PyObject* pmod, * pfun, * pargs, * preturn;
Py_Initialize();
// 判断Python虚拟机是否成功
if (Py_IsInitialized() == 0) {
cout<<"fal to initialize Python"<<endl;
return -1;
}
cout << "server start" << endl;
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('.')");
pmod = PyImport_ImportModule("my_main");//.py文件
if (!pmod) { cout << "module import error" << endl; }
pfun = PyObject_GetAttrString(pmod, "callTest");//函数名
if (!pfun)cout << "cfun load errot" << endl;
pargs = PyTuple_New(1);
PyObject* o1 = PyCodec_Encode(Py_BuildValue("s", "test"), "utf-8", NULL);
PyTuple_SetItem(pargs, 0, o1);
preturn = PyObject_CallObject(pfun, pargs);
char* result = new char[20];
PyArg_Parse(preturn, "s", &result);
cout << result <<endl;
// 退出Python虚拟机
Py_Finalize();
cout << "server end" << endl;
return 0;
}
中文来了
但是上边的函数但凡有中文返回值或者中文参数就会报错或者乱码
解决方式这里是详细的中文参数的教程
需要Qt库这里是VS导入的教程
最好我们的代码如下:
#include <stdio.h>
#include <Python.h>
#include <iostream>
#include "GBK.h"
using namespace std;
int main() {
// 初始化Python虚拟机
PyObject* pmod, * pfun, * pargs, * preturn;
Py_Initialize();
// 判断Python虚拟机是否成功
if (Py_IsInitialized() == 0) {
cout<<"fal to initialize Python"<<endl;
return -1;
}
cout << "server start" << endl;
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('.')");
pmod = PyImport_ImportModule("my_main");
if (!pmod) { cout << "module import error" << endl; }
pfun = PyObject_GetAttrString(pmod, "fanyi_youdao_API_21");
if (!pfun)cout << "cfun load errot" << endl;
pargs = PyTuple_New(1);
PyObject* o1 = PyCodec_Encode(Py_BuildValue("s", "Time is the only true unit of measure"), "utf-8", NULL);
PyTuple_SetItem(pargs, 0, o1);
preturn = PyObject_CallObject(pfun, pargs);
char* result = new char[20];
PyArg_Parse(preturn, "s", &result);
string reStr = GBK::FromUnicode(result);
cout << reStr <<endl;
// 退出Python虚拟机
Py_Finalize();
cout << "server end" << endl;
return 0;
}
结果如下:
更改为中文同样是可以的
PyObject* o1 = PyCodec_Encode(Py_BuildValue("s", "时间是衡量一切的标准。"), "utf-8", NULL);
这里笔者没有对中文字符decode但是这并不是重点.二者是等价的.
到此就是本节的全部内容了,python的c++接口确实好用,但是还是推荐用Json来数据传输.毕竟c++接口太复杂了,一时间根本无法掌握,各种帖子教程版本不一,很难一时间跑到正确的代码.
所以说不同语音的交互要尽量使用文件交互,不要想着拿接口