Qt调用python方法
1 接口
上面我们提到过要让机器“学习”,一般需要:
- 用来解决问题的模型model
- 学习数据(或者说训练数据)data
存放在列表,表格中;
- 让模型model通过数据data学会解决特定问题的学习算法learn
调试:
如果python程序中包含了其他第三方库,则调用极可能出错,且不易查明原因。如笔者曾用c程序调用包含tensorflow的python程序,有的可以成功,有的则卡在某条语句,百般尝试也未能实现。
在调用含有第三方库的python程序时,首先需要保证在cmd下运行python程序无误。
C++调用例子代码正常;
2 方法汇总
2.1.外部工具法Qt creator
https://blog.csdn.net/alxe_made/article/details/83382159
2.2.Python官方提供的与C++交互编程方法。
1 运行python代码例子
#define PY_SSIZE_T_CLEAN #include <Python.h> int main(int argc, char *argv[]) { wchar_t *program = Py_DecodeLocale(argv[0], NULL); if (program == NULL) { fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); exit(1); } Py_SetProgramName(program); /* optional but recommended */ Py_Initialize(); PyRun_SimpleString("from time import time,ctime\n" "print('Today is', ctime(time()))\n"); if (Py_FinalizeEx() < 0) { exit(120); } PyMem_RawFree(program); return 0; }
问题:无法打开包括文件:“python.h”: No such file or directory
说明bat里面和pro文件没把python的include目录等告诉项目;
问题:无法打开包括文件:“inttypes.h”: No such file or directory
解决方法:删除之,并在其之前添加如下代码:
#if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
# define CONFIG_WIN32
#endif
#if defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(EMULATE_INTTYPES)
# define EMULATE_INTTYPES
#endif
#ifndef EMULATE_INTTYPES
# include <inttypes.h>
#else
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
# ifdef CONFIG_WIN32
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
# else /* other OS */
typedef signed long long int64_t;
typedef unsigned long long uint64_t;
# endif /* other OS */
#endif /* EMULATE_INTTYPES */
LNK1104: 无法打开文件“python38_d.lib”
有3个方案:
- 将python36.lib复制一份,重命名为python36_d.lib
- 修改pyconfig.h文件(奇怪,我在3.8说的文件没这个内容)。
参照网上的解决办法,是将目录Python35\include下的pyconfig.h文件中的
#ifdefined(_DEBUG)
//# pragma comment(lib,"python35_d.lib")
#pragmacomment(lib,"python34.lib") // no debug lib in python distribution
#elif...
#ifdef _DEBUG
//# define Py_DEBUG
#endif
黑体部分注释掉即可。不知会不会产生其他错误,现在标记一下。
LNK2019: 无法解析的外部符号 __imp__Py_FinalizeEx
等等
fatal error LNK1120: 10 个无法解析的外部命令
在3.6中解决inttypes.h的报错后,
我在vs中得配置平台是win32,而pthon2.7是64位的,将配置平台改为活动x64后编译通过(平台如何修改:右击项目 -> 属性->最上面的平台修改即可)。
3.下载python的源代码,用VS重新编译;
在解决这个问题之前,请先确保你安装了python的debug版本,如果没有,可以修改已经安装的python,如下图所示
勾选最后一项,然后install
然后,解决方法很简单,给如下图所示的项目添加python37_d.lib所在的路径即可
【安装失败,估计是VS2008太老了,安装VS2017看看】
安装VS2017后,发现还是不行,打开软件安装日志查看是安装包没下载,人工下载后,安装失败;
Error 0x80072f7d: Failed to send request to URL: https://www.python.org/ftp/python/3.8.1/win32/core_pdb.msi, trying to process HTTP status code anyway.
VS2017安装的时候安装了python3.6的64位版本,用3.6看看;
下载源代码;
1)命令行进入到解压目录的PCbuild,如X:\Python-2.7.16\PCbuild
2)运行get_externals.bat,下载外部库,如图所示
我的运行失败;
0x03 编译
1)打开PCbuild目录的pcbuild.sln,根据需要选择后点“确定”,如图所示:
2)先选择python、pythoncore的win32 debug 版本测试编译,如图所示:
3)编译失败,需要修改相关标识符
C1189 #error: "No Target Arc
定位错误位置在winnt.h
#if defined(_AMD64_) || defined(_X86_) #define PROBE_ALIGNMENT( _s ) TYPE_ALIGNMENT( DWORD ) #elif defined(_IA64_) || defined(_ARM_) || defined(_ARM64_) #define PROBE_ALIGNMENT( _s ) TYPE_ALIGNMENT( DWORD ) #elif !defined(RC_INVOKED) #error "No Target Architecture" #endif
原因也很简答,就是没有定义 (_AMD64_) 、(_X86_)这几个宏,编译器检查到,就抛出异常
我选择在项目中添加_AMD64_宏(其他三个也可以)解决该问题。
具体方法:项目名称右键-->属性-->C/C++-->预处理器-->添加宏,如下所示:
4)timezone改为_timezone , daylight改为daylight , tzname改为tzname后,重新编译[未出现]
1 #ifdef PYOS_OS2 2 PyModule_AddIntConstant(m, "timezone", _timezone); 3 #else /* !PYOS_OS2 */ 4 PyModule_AddIntConstant(m, "timezone", _timezone); 5 #endif /* PYOS_OS2 */ 6 #ifdef HAVE_ALTZONE 7 PyModule_AddIntConstant(m, "altzone", altzone); 8 #else 9 #ifdef PYOS_OS2 10 PyModule_AddIntConstant(m, "altzone", _timezone-3600); 11 #else /* !PYOS_OS2 */ 12 PyModule_AddIntConstant(m, "altzone", _timezone -3600); 13 #endif /* PYOS_OS2 */ 14 #endif 15 PyModule_AddIntConstant(m, "daylight", _daylight); 16 PyModule_AddObject(m, "tzname", 17 Py_BuildValue("(zz)", _tzname[0], _tzname[1]));
5)编译仍然失败,如下图所示:
6)找到pythoncore 工程的posixmodule.c , 修改_PyVerify_fd如下[未出现]
/* This function emulates what the windows CRT does to validate file handles */ int _PyVerify_fd(int fd) { // const int i1 = fd >> IOINFO_L2E; // const int i2 = fd & ((1 << IOINFO_L2E) - 1); // static int sizeof_ioinfo = 0; // /* Determine the actual size of the ioinfo structure, // * as used by the CRT loaded in memory // */ // if (sizeof_ioinfo == 0 && __pioinfo[0] != NULL) { // sizeof_ioinfo = _msize(__pioinfo[0]) / IOINFO_ARRAY_ELTS; // } // if (sizeof_ioinfo == 0) { // /* This should not happen... */ // goto fail; // } // /* See that it isn't a special CLEAR fileno */ // if (fd != _NO_CONSOLE_FILENO) { // /* Microsoft CRT would check that 0<=fd<_nhandle but we can't do that. Instead // * we check pointer validity and other info // */ // if (0 <= i1 && i1 < IOINFO_ARRAYS && __pioinfo[i1] != NULL) { // /* finally, check that the file is open */ // my_ioinfo* info = (my_ioinfo*)(__pioinfo[i1] + i2 * sizeof_ioinfo); // if (info->osfile & FOPEN) { // return 1; // } // } // } //fail: // errno = EBADF; //a call to _get_osfhandle with invalid fd sets errno to EBADF if (_get_osfhandle(fd) == INVALID_HANDLE_VALUE) return 0; else return 1; return 0; }
7)再次编译,编译成功。
8)根据需要选择相应库进行编译
Python-3.6.10\PCbuild打开python.vcxproj
以下这两个操作必不可少!
1、将python37_d.lib所在文件夹的路径添加到库目录里面
2、然后在附加依赖项中添加python37_d.lib,就是刚才找不到的这个库文件的名字
3、重新右键生成opencv_python3,问题解决!发现生成的lib文件在本地d盘,没在c盘;
SyntaxError: (unicode error) 'utf-8' codec can't decode byte 0xc1 in position 0: invalid start byte
python中默认的编码格式是 utf-8,因此需要将编辑的文件格式进行转换为utf-8,只需对Geany软件进行如下设置:
在菜单栏中选择“文档”中的“设置文件编码”选择“Unicode(UTF-8)”,如图
无法打开包括文件:“pyconfig.h”: No such file or directory
文件对比发现VS2017配套安装的3.6是有这个文件的;
集成到组态计算软件中,报错:
pyconfig.h:ssize_t重定义
解决方法:
typedef _W64 int ssize_t_px;
继续编辑
include\object.h(190) : error C2059: 语法错误 : ";"
解决办法:
注释该行: //PyType_Slot *slots; /* terminated by slot==0. */
2. 参数传递
参数传递的意思就是将 C 的值传递给 py 的函数,然后进行计算输出。
将数据值从C转换为Python,
使用转换后的值对Python接口例程执行函数调用
将数据值从Python调用转换为C。
2. 例子
虽然该程序的功能相当大,但是大部分代码用于Python和C之间的数据转换以及错误报告。
第二个程序的目标是在Python脚本中执行一个函数,现在这里需要传递参数。与关于非常高级接口的部分一样,Python解释器并不直接与应用程序交互(但这将在下一节中进行更改)。
运行Python脚本中定义的函数的代码是:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <QCoreApplication>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
PyObject *pName, *pModule, *pFunc;
PyObject *pArgs, *pValue;
int i;
if (argc < 3) {
fprintf(stderr,"用法: call pythonfile funcname [args]\n");
return 1;
}
Py_Initialize(); //初始化 Py 解释器
PyRun_SimpleString("import sys\n");
PyRun_SimpleString("print(sys.path.append('/Users/wangxinnian/Downloads/qtApp/testQP2'))\n");
pName = PyUnicode_DecodeFSDefault(argv[1]);
/* Error checking of pName left out */
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != nullptr) {
pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */
if (pFunc && PyCallable_Check(pFunc)) {
pArgs = PyTuple_New(argc - 3);
for (i = 0; i < argc - 3; ++i) {
pValue = PyLong_FromLong(atoi(argv[i + 3]));
if (!pValue) {
Py_DECREF(pArgs);
Py_DECREF(pModule);
fprintf(stderr, "不能转化参数\n");
return 1;
}
/* pValue reference stolen here: */
PyTuple_SetItem(pArgs, i, pValue);
}
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != nullptr) {
printf("Result of call: %ld\n", PyLong_AsLong(pValue));
Py_DECREF(pValue);
}
else {
Py_DECREF(pFunc);
Py_DECREF(pModule);
PyErr_Print();
fprintf(stderr,"Call failed\n");
return 1;
}
}
else {
if (PyErr_Occurred())
PyErr_Print();
fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);
}
else {
PyErr_Print();
fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
return 1;
}
if (Py_FinalizeEx() < 0) {
return 120;
}
return a.exec();
}
无法解析的外部符号 __imp___Py_RefTotal
无法解析的外部符号 __imp___Py_NegativeRefcount,该符号在函数 __import_array 中被引用
这是由于安装的python为Release版本,而你的C++项目为Debug模式,因此更改你的项目为Release模式。另外需要注意,区分32位和64位;然后重新编译;
(2)Python脚本文件MyMultiply.py代码,保存到代码指定的目录,没指定的话放c++的生成exe目录:
def multiply(a,b):
print("Will compute", a, "times", b)
c = 0
for i in range(0, a):
c = c + b
return c
(3)VS命令行参数:MyMultiply multiply 5 3
(4)程序运行结果:
例子3
/*
* test.cpp
* Created on: 2010-8-12
* Author: lihaibo
*/
#include <C:/Python27/include/Python.h>
#include <iostream>
#include <string>
int main(void) {
Py_Initialize(); // 启动虚拟机
if (!Py_IsInitialized())
return -1;
// 导入模块
PyObject* pModule = PyImport_ImportModule("testpy");
if (!pModule) {
printf("Cant open python file!/n");
return -1;
}
// 模块的字典列表
PyObject* pDict = PyModule_GetDict(pModule);
if (!pDict) {
printf("Cant find dictionary./n");
return -1;
}
// 演示函数调用
PyObject* pFunHi = PyDict_GetItemString(pDict, "sayhi");
PyObject_CallFunction(pFunHi, "s", "lhb");
Py_DECREF(pFunHi);
// 演示构造一个Python对象,并调用Class的方法
// 获取Second类
PyObject* pClassSecond = PyDict_GetItemString(pDict, "Second");
if (!pClassSecond) {
printf("Cant find second class./n");
return -1;
}
//获取Person类
PyObject* pClassPerson = PyDict_GetItemString(pDict, "Person");
if (!pClassPerson) {
printf("Cant find person class./n");
return -1;
}
//构造Second的实例
PyObject* pInstanceSecond = PyInstance_New(pClassSecond, NULL, NULL);
if (!pInstanceSecond) {
printf("Cant create second instance./n");
return -1;
}
//构造Person的实例
PyObject* pInstancePerson = PyInstance_New(pClassPerson, NULL, NULL);
if (!pInstancePerson) {
printf("Cant find person instance./n");
return -1;
}
//把person实例传入second的invoke方法
PyObject_CallMethod(pInstanceSecond, "invoke", "O", pInstancePerson);
//释放
Py_DECREF(pInstanceSecond);
Py_DECREF(pInstancePerson);
Py_DECREF(pClassSecond);
Py_DECREF(pClassPerson);
Py_DECREF(pModule);
Py_Finalize(); // 关闭虚拟机
return 0;
}
例子4:tensorflow第三方库
在调用含有第三方库的python程序时,首先需要保证在cmd下运行python程序无误。环境配置如上文所述,但是很多时候程序在
pModule = PyImport_ImportModule("test3");//Python文件名
这一行出错,pModule 结果为null,原因通常是import tensorflow as tf这一句未能成功调用,将需调用的python程序中只保留这一句,可轻松验证。
经探索,可在原cpp文件中加入命令行参数即可成功运行。
/**
*Copyright @ 2019 Zhang Peng. All Right Reserved.
*Filename:
*Author: Zhang Peng
*Date:
*Version:
*Description:
**/
#include<iostream>
#include <Python.h>
using namespace std;
int main()
{
//***python调用***//
//初始化python模块
Py_Initialize();
// 检查初始化是否成功
if (!Py_IsInitialized()) {
printf("py_initialize failed");
}
PyObject *pModule;
PyObject*pFunc = NULL;
PyObject*pArg = NULL;
//main命令行参数
int argc = 1;
wchar_t * argv[] = { L" " };
PySys_SetArgv(argc, argv); //加入argv参数 否则出错
pModule = PyImport_ImportModule("test3");//Python文件名
if (!pModule) {
printf("cannot open module!");
Py_Finalize();
return 0;
}
else
{
pFunc = PyObject_GetAttrString(pModule, "run");//Python文件中的函数名
if (!pFunc) {
printf("cannot open FUNC!");
Py_Finalize();
return 0;
}
else
{
PyObject* pPath = Py_BuildValue("(s)", "7513.jpg");
pArg = PyEval_CallObject(pFunc, pPath);//调用函数
int result;
PyArg_Parse(pArg, "i", &result);
cout << "预测结果: " << result << endl;
}
}
system("pause");
return 0;
}
以上代码中重要的地方就是加入以下几行代码
//main命令行参数
int argc = 1;
wchar_t * argv[] = { L" " };
PySys_SetArgv(argc, argv); //加入argv参数 否则出错
————————————————
4 代码总结
第1步是导入.py文件:
Python虚拟机的初始化及退出
初始化Python虚拟机需要调用Py_Initialize()来实现。
Py_IsInitialized()用于判断Python虚拟机初始化是否成功,True是成功,False是失败。
C/C++中调用Python之前必须先初始化虚拟机。
第2步是导入.py文件:
使用PyObject* pModule来存储导入的.py文件模块, 调用的方法是PyImport_ImportModule(path): PyObject* pModule = PyImport_ImportModule("testpy");
使用PyObject* pDict来存储导入模块中的方法字典, 调用的方法是PyModule_GetDict(module): PyObject* pDict = PyModule_GetDict(pModule);
这样就完成了.py文件的导入.
第3步是导入已导入模块中的方法或类:
获取方法, 调用的方法是PyDict_GetItemString(dict, methodName): PyObject* pFunHi = PyDict_GetItemString(pDict, "sayhi");
获取类, 调用的方法同上, 注意红体部分的字符串对应于.py文件中的类/方法名: PyObject* pClassSecond = PyDict_GetItemString(pDict, "Second");
第4步是使用导入的方法或类:
使用方法, 调用PyObject_CallFunction(pFunc, "s", args)即可: PyObject_CallFunction(pFunHi, "s", "lhb");
使用类构造对象, 调用PyInstance_New(pClass, NULL, NULL)即可: PyObject* pInstanceSecond = PyInstance_New(pClassSecond, NULL, NULL); , 注意其中的pClassSecond为第二步.2中获取的类指针
使用类对象的方法, 调用PyObject_CallMethod(pInstance, methodname, "O", args)即可: PyObject_CallMethod(pInstanceSecond, "invoke", "O", pInstancePerson);
上述调用中的"s"和"O"代表的是参数列表的类型, 我们可以在 Py_BuildValue 找到所有的类型, 本文最后也附了此表.
第5部 销毁和退出
最后不要忘记销毁这些对象: Py_DECREF(pointer);
退出虚拟机的时候调用Py_Finalize()。
进程退出时要退出Python虚拟机。
PyObject
Python的所有对象类型都是此类型的扩展。 这是一种类型,它包含Python将对象的指针视为对象所需的信息。 在正常的“发布”版本中,它仅包含对象的引用计数和指向相应类型对象的指针。 实际上没有任何东西被声明为PyObject,但是每个指向Python对象的指针都可以转换为PyObject *。 必须使用宏Py_REFCNT和Py_TYPE来访问成员。
宏描述,不包括全部
Py_TYPE: 获取Python对象的数据类型 Py_REFCNT: Python的引用计数器 Py_SIZE: 获取Python数据大小 还有很多...
Py_BuildValue
可以使用其将C的所有基本数据类型转换成Python可访问的数据类型。
标识符介绍:
s(str或None)[char *] 使用'utf-8'编码将以null结尾的C字符串转换为Python str对象。如果C字符串指针为NULL,则表示None。 s#(str或None)[char *,int] 使用'utf-8'编码将C字符串及其长度转换为Python str对象。如果C字符串指针为NULL,则忽略长度返回None。 y(字节)[char *] 这会将C字符串转换为Python字节对象。如果C字符串指针为NULL,则返回None。 y#(字节)[char *,int] 这会将C字符串及其长度转换为Python对象。如果C字符串指针为NULL,则返回None。 z(str或None)[char *] 与s相同。 z#(str或None)[char *,int] 与s#相同。 u(str)[Py_UNICODE *] 将Unicode(UCS-2或UCS-4)数据的以null结尾的缓冲区转换为Python Unicode对象。如果Unicode缓冲区指针为NULL,则返回None。 u#(str)[Py_UNICODE *,int] 将Unicode(UCS-2或UCS-4)数据缓冲区及其长度转换为Python Unicode对象。如果Unicode缓冲区指针为NULL,则忽略长度并返回None。 U(str或None)[char *] 与s相同。 U#(str或None)[char *,int] 与s#相同。 i(int)[int] 将普通的C int转换为Python整数对象。 b(int)[char] 将纯C char转换为Python整数对象。 h(int)[short int] 将普通的C short int转换为Python整数对象。 l(int)[long int] 将C long int转换为Python整数对象。 B(int)[unsigned char] 将C unsigned char转换为Python整数对象。 H(int)[unsigned short int] 将C unsigned short int转换为Python整数对象。 I(int)[unsigned int] 将C unsigned int转换为Python整数对象。 k(int)[unsigned long] 将C unsigned long转换为Python整数对象。 L(int)[long long] 将C long long转换为Python整数对象。 K(int)[unsigned long long] 将C unsigned long long转换为Python整数对象。 n(int)[Py_ssize_t] 将C Py_ssize_t转换为Python整数。 c(长度为1的字节)[char] 将表示字节的C int转换为长度为1的Python字节对象。 C(长度为1的str)[int] 将表示字符的C int转换为长度为1的Python str对象。 d(float) [double] 将C double转换为Python浮点数。 f(float) [float] 将C float转换为Python浮点数。 D(complex) [Py_complex *] 将C Py_complex结构转换为Python复数。 O(object) [PyObject *] 不改变Python对象的传递(引用计数除外,它增加1)。如果传入的对象是NULL指针,则假定这是因为产生参数的调用发现错误并设置了异常。因此,Py_BuildValue()将返回NULL但不会引发异常。如果尚未引发异常,则设置SystemError。 S(object) [PyObject *] 与O相同 N((object) [PyObject *] 与O相同,但不会增加对象的引用计数。通过调用参数列表中的对象构造函数创建对象时很有用。 O&(object) [converter, anything] 通过转换器函数将任何内容转换为Python对象。该函数被调用任何东西(应与void *兼容)作为其参数,并应返回“新”Python对象,如果发生错误则返回NULL。 (items) (tuple) [matching-items] 将一系列C值转换为具有相同项目数的Python元组。 [items](list) [matching-items] 将一系列C值转换为具有相同项目数的Python列表。 {items}(dict) [matching-items] 将一系列C值转换为Python字典。每对连续的C值将一个项添加到字典中,分别用作键和值。 如果格式字符串中存在错误,则设置SystemError异常并返回NULL。
void int_object(){ // 第一种方式 PyObject *py_ival = Py_BuildValue("i", -5987); // Python有符号整型 PyObject *py_ival2 = PyLong_FromLong(-8979); int ival = PyLong_AsLong(py_ival); // 把Python有字符整型转换成C的有字符整型 int ival2 = PyLong_AsLong(py_ival2); // 把Python有字符整型转换成C的有字符整型 printf("ival = %d, ival2 = %d\n", ival, ival2); // 第二种方式 PyObject *py_uval = Py_BuildValue("I", 465486); // Python无符号整型 PyObject *py_uval2 = PyLong_FromUnsignedLong(1654864); unsigned int uval = PyLong_AsUnsignedLong(py_uval); // 把Python无字符整型转换成C的无字符整型 unsigned int uval2 = PyLong_AsUnsignedLong(py_uval2); // 把Python无字符整型转换成C的无字符整型 printf("uval = %u, uval2 = %u\n", uval, uval2); }
创建长整型的Python对象
void long_object(){ // 第一种方式 PyObject *py_lval = Py_BuildValue("L", 45648946484984); // Python 长整型 long long c_lval = PyLong_AsLongLong(py_lval); // 转换成C的长整型 printf("clval = %lld\n", c_lval); // 第二种方式 PyObject *py_lval2 = PyLong_FromLongLong(234234623454525); // PyLong_FromLongLong 使用方法定义一个Python长整型 long long c_lval2 = PyLong_AsLongLong(py_lval2); // 转换成C的长整型 printf("clval2 = %lld\n", c_lval2); }
创建浮点类型的Python对象
void double_object(){ // 第一种方式 float fval = 632.045; PyObject *py_fval = Py_BuildValue("d", fval); // Python 浮点类型 float c_fval = PyFloat_AsDouble(py_fval); // C的浮点类型 printf("fval = %f\n", c_fval); // 第二种方式 double dval = 48941546.578; PyObject *py_dval = PyFloat_FromDouble(dval); // Python 浮点类型 double c_dval = PyFloat_AsDouble(py_dval); // C的浮点类型 printf("c_dval = %lf\n", c_dval); }
创建布尔类型对象
void boolean_object(){ // 第一种方式 bool bval = true; // false 反之 PyObject *py_bval = Py_BuildValue("b", bval); // Python 布尔类型 int c_bval = PyInt_AsLong(py_bval); printf("c_bval = %d\n", c_bval); // 第二种方式 bool bval2 = false; PyObject *py_bval2 = PyBool_FromLong(bval2); // Python 布尔类型 int c_bval2 = PyInt_AsLong(py_bval2); printf("c_bval2 = %d\n", c_bval2); }
创建Python string对象
void string_object(){ // 第一种方式 const char *pstr = "this is a test"; PyObject *py_str = Py_BuildValue("s", pstr); // Python 字符串对象 char *c_pstr = PyString_AsString(py_str); // 转成C的字符指针 printf("c_pstr = %s\n", c_pstr); // 第二种方式 const char *pstr2 = "this is a test1"; PyObject *py_str2 = PyString_FromString(pstr2); // Python 字符串对象 char *c_pstr2 = PyString_AsString(py_str2); // 转成C的字符指针 printf("c_pstr2 = %s\n", c_pstr2); // 创建一个二进制的字符串对象 // 第一种方式 const int mem_len = 1024; char *mem = new char[mem_len]; PyObject *py_mem = Py_BuildValue("s#", mem, mem_len); // 1. 数据的类型 2. 指向数据的指针 3. 数据的长度 int c_data_len = PyString_Size(py_mem); printf("c_data_len = %d\n", c_data_len); // 第二种方式 PyObject *py_mem2 = PyString_FromStringAndSize(mem, mem_len); int c_data_len2 = PyString_Size(py_mem2); printf("c_data_len2 = %d\n", c_data_len2); }
创建unicode字符串对象
void unicode_object(){ const char *p_ustr = "兰玉磊"; PyObject *py_unicode = PyUnicode_FromString(p_ustr); // 把C的字符串转成Python的unicode // 把unicode转成C的字符串 PyObject *py_utf8 = PyUnicode_AsUTF8String(py_unicode); // 把unicode转成utf-8 const char *c_string = PyString_AsString(py_utf8); // 把utf-8转成c的字符串 printf("c_utf8 = %s\n", c_string); // 格式化unicode字符串 // 创建一个unicode字符串 PyObject *py_unicode_fmt = PyUnicode_FromFormat("%s%d%s", "我今年", 18, "岁"); // 把unicode转C字符串 PyObject *py_utf8_fmt = PyUnicode_AsUTF8String(py_unicode_fmt); const char *utf8_fmt = PyString_AsString(py_utf8_fmt); printf("utf8_fmt = %s\n", utf8_fmt); }
使用Py_None
Py_None是一个全局的变量
PyObject* none_object(){ Py_RETURN_NONE; // 不需要自己return }
Python List API
List API 简单介绍
int PyList_Check(PyObject *p) 判断是否是一个Python List(列表)
PyObject* PyList_New(Py_ssize_t len) 创建一个列表
Py_ssize_t PyList_Size(PyObject *list) 获取列表元素的个数 len(list)
Python Dict API
Dict API 简单介绍
int PyDict_Check(PyObject *p) 判断对象是不是一个字典
PyObject* PyDict_New() 创建一个Python对象
void PyDict_Clear(PyObject *p) 清空Python对象的数据
// 生成Python可调用的动态链接库 BOOST_PYTHON_MODULE(gpassword){ class_<GeneratePassword> ("gpassword", init<int>()) .def("getPassword", &GeneratePassword::getPassword);
矩阵
2.3.2 numpy基本数据类型
numpy中的数据类型被称为ndarray(即 N-dimensional array,多维数组),创建一个ndarray很简单:
- import numpy as np
- array=np.array([1,2,3],dtype=np.uint8)
- print array
即向np.array()函数传入一个python列表即可。注意dtype参数是可选的,它指定了生成的数组的数据长度和类型,这里是长度为8bit的无符号整数。
2.3.3 快速创建矩阵
mat1=np.zeros((2,3))
np.zeros()快速创建一个指定维度的全0矩阵,注意传进去的参数是一个tuple。
2.3.4 numpy中的高维矩阵
"矩阵"一般指有行和列的“二维”矩阵,但numpy还支持高维矩阵,比如下面:
- nd=np.zeros((1,2,3,4))
- print nd.shape
- print nd.size
nd就可以看作是一个1x2x3x4尺寸的高维矩阵。ndarray.shape保存的是数组的“形状”,也就是高维矩阵每一维的长度。ndarray.size保存的是数组每一维长度相乘的结果,即数组元素的个数。
初始化解释器之后,使用PyImport_Import()加载脚本。这个例程需要一个Python字符串作为参数,它是使用PyUnicode_FromString()数据转换例程构造的。
加载脚本之后,使用PyObject_GetAttrString()检索我们要查找的名称。如果名称存在,并且返回的对象是可调用的,则可以安全地假设它是一个函数。然后程序按照正常方式构造一个参数元组。然后用以下方法调用Python函数。
在函数返回时,pValue要么为NULL,要么包含对函数返回值的引用。请确保在检查值之后释放引用。
————————————————
版权声明:本文为CSDN博主「caridle」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/caridle/article/details/97366903
2.3 使用PythonQt库
https://www.cnblogs.com/xia-weiwen/p/7932379.html
2.4 boost::python
boost::python库是pyhon和c++相互交互的框架,可以再python中调用c++的类和方法,也可以让c++调用python的类和方法
python自身提供了一个Python/C API用来实现python和c++的交互,boost::python是Python/C API的包装,所以用起来更简单一些。
关于python与C++混合编程,事实上有两个部分
- extending 所谓python 程序中调用c/c++代码, 其实是先处理c++代码, 预先生成的动态链接库, 如example.so, 而在python代码中import example;即可使用c/c++的函数 .
- embedding c++代码中调用 python 代码.
Python对一个动态类型的语言,C++是一个静态类型的语言,对于Python中的变量类型,Boost.Python都有相应的类对应,他们都是boost::python::object的子类。boost::python::object 包装了PyObject *, 通过这种方式c++可以平滑的操作python对象。Boost.Python的主要目标既保持Python的编程风格同时又提供C++和Python的双向映射。
Python::Object对应一个Python对象,那么c++怎么获取Python对象的值呢,这个需要使用Python::extract方法
- double d=python::extract<double>(obj)
通过PyObject*构造一个boost::python::object对象。
boost::python::object o(boost::python::handle<>(pyobj));
这样情况下. o管理pyobj, 不会增加pyobj的引用计数。
- boost::python::object o(boost::python::handle<>(boost::python::borrowed(pyobj)));
这中情况下,会调用Py_INCREF ,当O对象生命周期结束 ,pyobj不会析析构掉
怎么从python::object对象中获取PyObject* :
- obj.ptr()
#include <iostream>
using namespace std;
#include <boost/python.hpp>
using namespace boost::python;
int main(int argc, char *argv[])
{
Py_Initialize();
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");
exec("hello = file('hello.txt', 'w')\n"
"hello.write('Hello world!')\n"
"hello.close()",
main_namespace);
exec("result = 5 ** 2", main_namespace);
int five_squared = extract<int>(main_namespace["result"]);
cout << "The five_squeared caculated by python is " << five_squared << endl;
// Load the sys module.
object sys = import("sys");
// Extract the python version.
std::string version = extract<std::string>(sys.attr("version"));
std::cout << version << std::endl;
//要求simple.py与可执行文件在相同路径下! 运行ok
object simple = exec_file("simple.py",main_namespace, main_namespace);
//dict global;
//object result = exec_file("simple.py", global, global);
object foo = main_namespace["foo"];
int val = extract<int>(foo(5));
cout << "Python has caculated foo as " << val << endl;
return 0;
}
如何编译这个程序呢,其实就是能找的python, 以及boost.python的头文件和库文件.我采用了cmake,管理.
最简单的在相同代码路径下,建立一个文件CMakeLists.txt,写入以下内容,注意你可能需要根据自己的安装路径进行
更改.
project(Embedding) #随便起一个工程名称
#boost.python,python头文件路径
include_directories(/usr/local/boost1.4/include /usr/local/include/python2.6)
link_directories(/usr/local/boost1.4/lib /usr/local/lib) #boost.python,python动态链接库路径
add_executable (embedding embedding.cc) #源文件embedding.cc 生成可执行文件embedding
target_link_libraries(embedding libboost_python.so libpython2.6.so) #可执行文件依赖与这两个库