C/C++调用python接口入门(1-全是坑)

@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即可.如下表

类型第一个参数对应的转化
intPy_BuildValue(“i”, 233)PyLong_AsLong(p_val1)
unsign intPy_BuildValue(“I”, 233)大写iPyLong_AsUnsignedLong(p_val1)
long longPy_BuildValue(“l”, 233333333333)小写LPyLong_AsLongLong(p_val1)
float/doublePy_BuildValue(“d”, 233.3)PyFloat_AsDouble(p_val1)
boolPy_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++接口太复杂了,一时间根本无法掌握,各种帖子教程版本不一,很难一时间跑到正确的代码.
所以说不同语音的交互要尽量使用文件交互,不要想着拿接口

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值