C++调用Python浅析
环境
VS2005Python2.5.4 Windows XP SP3
简述
一般开发过游戏的都知道Lua和C++可以很好的结合在一起,取长补短,把Lua脚本当成类似动态链接库来使用,很好的利用了脚本开发的灵活性。而作为一门流行的通用型脚本语言python,也是可以做到的。在一个C++应用程序中,我们可以用一组插件来实现一些具有统一接口的功能,一般插件都是使用动态链接库实现,如果插件的变化比较频繁,我们可以使用Python来代替动态链接库形式的插件(堪称文本形式的动态链接库),这样可以方便地根据需求的变化改写脚本代码,而不是必须重新编译链接二进制的动态链接库。灵活性大大的提高了。
Python/CAPI简介
通过C++调用Python脚本主要要用到如下的一些Python提供的API,因为实际上C++要调用的是Python的解释器,而Python解释器本质就是实现在动态链接库里面的,因此在调用前和调用后要进行一些初始化和资源释放的工作,另外,要调用Python脚本里面的函数等等东西,需要Python提供的一些特殊API来包装C++调用。(可以参考[2])。
void Py_Initialize(void)
初始化Python解释器,如果初始化失败,继续下面的调用会出现各种错误,可惜的是此函数没有返回值来判断是否初始化成功,如果失败会导致致命错误。
int Py_IsInitialized(void)
检查是否已经进行了初始化,如果返回0,表示没有进行过初始化。
void Py_Finalize()
反初始化Python解释器,包括子解释器,调用此函数同时会释放Python解释器所占用的资源。
int PyRun_SimpleString(const char *command)
实际上是一个宏,执行一段Python代码。
PyObject* PyImport_ImportModule(char *name)
导入一个Python模块,参数name可以是*.py文件的文件名。类似Python内建函数import。
PyObject* PyModule_GetDict( PyObject *module)
相当于Python模块对象的__dict__属性,得到模块名称空间下的字典对象。
PyObject* PyRun_String(const char* str, int start,PyObject* globals, PyObject* locals)
执行一段Python代码。
int PyArg_Parse(PyObject* args, char* format, ...)
把Python数据类型解析为C的类型,这样C程序中才可以使用Python里面的数据。
PyObject* PyObject_GetAttrString(PyObject *o, char*attr_name)
返回模块对象o中的attr_name 属性或函数,相当于Python中表达式语句,o.attr_name。
PyObject* Py_BuildValue(char* format, ...)
和PyArg_Parse刚好相反,构建一个参数列表,把C类型转换为Python对象,使得Python里面可以使用C类型数据。
PyObject* PyEval_CallObject(PyObject* pfunc, PyObject*pargs)
此函数有两个参数,而且都是Python对象指针,其中pfunc是要调用的Python 函数,一般说来可以使用PyObject_GetAttrString()获得,pargs是函数的参数列表,通常是使用Py_BuildValue()来构建。
更多的API请参考官方的文档,比较直观简单,譬如怎样初始化一个类实例,怎样调用类成员函数。下面上点代码,感受下这个过程。
C++代码
#include "stdafx.h"
#include "Python.h"
int _tmain(int argc, _TCHAR* argv[])
{
int nRet = -1;
PyObject* pName = NULL;
PyObject* pModule =NULL;
PyObject* pDict = NULL;
PyObject* pFunc = NULL;
PyObject* pArgs = NULL;
PyObject* pRet = NULL;
do
{
// 初始化Python
// 在使用Python系统前,必须使用Py_Initialize对其
// 进行初始化。它会载入Python的内建模块并添加系统路
// 径到模块搜索路径中。这个函数没有返回值,检查系统
// 是否初始化成功需要使用Py_IsInitialized。
Py_Initialize();
// 检查初始化是否成功
if (!Py_IsInitialized())
{
break;
}
// 添加当前路径
// 把输入的字符串作为Python代码直接运行,返回
// 表示成功,-1表示有错。大多时候错误都是因为字符串
// 中有语法错误。
PyRun_SimpleString("importsys");
PyRun_SimpleString("sys.path.append('./')");
// 载入名为PyPlugin的脚本
pName = PyString_FromString("PyPlugin");
pModule = PyImport_Import(pName);
if (!pModule)
{
printf("can't findPyPlugin.py\n");
break;
}
pDict = PyModule_GetDict(pModule);
if (!pDict)
{
break;
}
// 找出函数名为AddMult的函数
pFunc = PyDict_GetItemString(pDict, "AddMult");
if (!pFunc || !PyCallable_Check(pFunc))
{
printf("can't findfunction [AddMult]\n");
break;
}
pArgs = Py_BuildValue("ii", 12, 14);
PyObject* pRet = PyEval_CallObject(pFunc,pArgs);
int a = 0;
int b = 0;
if (pRet && PyArg_ParseTuple(pRet,"ii", &a,&b))
{
printf("Function[AddMult] call successful a + b = %d, a * b = %d\n", a, b);
nRet = 0;
}
if (pArgs)
Py_DECREF(pArgs);
if (pFunc)
Py_DECREF(pFunc);
// 找出函数名为HelloWorld的函数
pFunc = PyDict_GetItemString(pDict, "HelloWorld");
if (!pFunc || !PyCallable_Check(pFunc))
{
printf("can't findfunction [HelloWorld]\n");
break;
}
pArgs = Py_BuildValue("(s)", "magictong");
PyEval_CallObject(pFunc,pArgs);
} while (0);
if (pRet)
Py_DECREF(pRet);
if (pArgs)
Py_DECREF(pArgs);
if (pFunc)
Py_DECREF(pFunc);
if (pDict)
Py_DECREF(pDict);
if (pModule)
Py_DECREF(pModule);
if (pName)
Py_DECREF(pName);
Py_Finalize();
return 0;
}
Python代码
#!/usr/bin/python
import string
class CMyClass:
def HelloWorld(self):
print 'HelloWorld'
class SecondClass:
def invoke(self,obj):
obj.HelloWorld()
def HelloWorld(strName):
print "Hello ", strName
def Add(a, b, c):
return a + b + c
def AddMult(a, b):
"""
"""
print "in FunctionAddMult..."
print a
print b
return a + b, a * b
def StringToUpper(strSrc):
return string.upper(strSrc)
下面还有几个比较重要的问题需要解决,且听慢慢道来。
C++怎么向Python传递参数
C++向Python传参数是以元组(tuple)的方式传过去的,因此我们实际上就是构造一个合适的Python元组就可以了,要用到PyTuple_New,Py_BuildValue,PyTuple_SetItem等几个函数,其中Py_BuildValue可以有其它一些的替换函数。
PyObject* pyParams = PyTuple_New(2);
PyObject* pyParams1= Py_BuildValue("i",5);
PyObject* pyParams2= Py_BuildValue("i",6);
PyTuple_SetItem(pyParams,0, pyParams1);
PyTuple_SetItem(pyParams,1, pyParams2);
pRet = PyEval_CallObject(pFunc, pyParams);
也可以直接使用PyObject* Py_BuildValue(char *format, ...) 函数来直接来构造tuple,此函数的使用也很简单,记住一些转换的格式常量即可轻松进行转换(格式常量有点类似printf,参考[9])。譬如s 表示字符串,i表示整型变量,f表示浮点数,o表示一个Python对象等等。
Py_BuildValue("") None
Py_BuildValue("i",123) 123
Py_BuildValue("iii",123, 456, 789) (123, 456, 789)
Py_BuildValue("s","hello") 'hello'
Py_BuildValue("ss","hello", "world") ('hello', 'world')
Py_BuildValue("s#","hello", 4) 'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)",123) (123,)
Py_BuildValue("(ii)",123, 456) (123, 456)
Py_BuildValue("(i,i)",123, 456) (123, 456)
Py_BuildValue("[i,i]",123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii))(ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
C++怎么转换Python的返回值
Python传回给C++的都是PyObject对象,因此可以调用Python里面的一些类型转换API来把返回值转换成C++里面的类型。类似PyInt_AsLong,PyFloat_AsDouble这些系列的函数。Python比较喜欢传回一个元组,可以使用PyArg_ParseTuple这个函数来解析。这个函数也要用到上面的格式常量(参考[10])。还有一个比较通用的转换函数是PyArg_Parse,也需要用到格式常量,够不够强大,用了就知道了。
直接调用Python脚本文件——另一种调用方式
初始化,反初始化都一样,此种方式其实就是直接调用PyRun_SimpleString函数。
if(fp && PyRun_SimpleString("execfile('PyFile.py')") != 0)
{
fclose(fp);
printf("PyRun_SimpleFile(%s)failed!", szFile);
return -1;
}
还有一种方法是调用PyRun_SimpleFile()函数来直接运行一个Python文件,不过这种方式有点危险,因为这个API要求传入一个FILE指针,而微软的几个CRT版本FILE指针的定义有了变化,因此传入你使用VS2005编译的FILE指针或者其它版本的FILE极有可能崩溃,如果你想安全调用,最好是自己把Python的源代码使用和应用程序相同的环境一起编译出lib来使用。
char szFile[] = "PyFile.py";
FILE* fp = fopen(szFile, "r");
if(fp && PyRun_SimpleFile(fp,szFile) != 0)
{
fclose(fp);
printf("PyRun_SimpleFile(%s)failed!", szFile);
return -1;
}
参考文献
[1] python官网 http://www.python.org/
[2] python/c APIReference Manual http://docs.python.org/2/c-api/index.html
[3] 用C语言扩展python的功能 https://www.ibm.com/developerworks/cn/linux/l-pythc/
[4] C++扩展和嵌入Python http://www.vckbase.com/index.php/wv/1258
[5] Python调用C/C++模块 http://blog.csdn.net/masefee/article/details/4750920
[6] Extendingand Embedding the Python Interpreter
http://docs.python.org/2/extending/index.html
[7] EmbeddingPython in Another Application
http://docs.python.org/2/extending/embedding.html
[8] C调用Python类/函数简单代码
http://www.360doc.com/content/12/0506/13/9369336_209021809.shtml
[9] The Py_BuildValue()Function
http://docs.python.org/release/1.5.2p2/ext/buildValue.html
[10] FormatStrings for PyArg_ParseTuple()
http://docs.python.org/release/1.5.2p2/ext/parseTuple.html
[11]Python编程
http://wiki.woodpecker.org.cn/moin/PP3eD
http://blog.csdn.net/magictong/article/details/8947892
C++调用Python浅析的更多相关文章
- 浅析 C++ 调用 Python 模块
浅析 C++ 调用 Python 模块 作为一种胶水语言,Python 能够很容易地调用 C . C++ 等语言,也能够通过其他语言调用 Python 的模块. Python 提供了 C++ 库,使得 ...
- [转] C/C++ 调用Python
from : https://cyendra.github.io/2018/07/10/pythoncpp/ 目录 前言 官方文档 环境搭建 编译链接 Demo 解释器 初始化 GIL Object ...
- python 浅析模块,包及其相关用法
今天买了一本关于模块的书,说实话,模块真的太多了,小编许多也不知道,要是把模块全讲完,可能得出本书了,所以小编在自己有限的能力范围内在这里浅析一下自己的见解,同时讲讲几个常用的模块. 这里是2018. ...
- cpp 调用python
在用cpp调用python时, 出现致命错误: no module named site , 原因解释器在搜索路径下没有找到python库.可以在调用Py_Initialize前,调用 Py_Se ...
- c调用python
#include <Python.h>//python33(python2.x有几个函数不对应) /* PyImport_ImportModule 导入一个Python模块并返回它的指针 ...
- linux+php+apache web调用python脚本权限问题解决方案
lamp : linux + apache + mysql + php 在上篇随笔中linux+php+apache调用python脚本时出现的问题的根本原因是:apache运行时使用的apache用 ...
- linux+php+apache web调用python脚本权限问题
lamp : linux + apache + mysql + php 在近期项目中使用 linux + apache + php调用python脚本是出现以下权限问题: build/bdist.li ...
- C#中调用python方法
最近因为项目设计,有部分使用Python脚本,因此代码中需要调用python方法. 1.首先,在c#中调用python必须安装IronPython,在 http://ironpython.codepl ...
- PHP 调用Python脚本
上次做用户反馈自动翻译,写了个python脚本,将日文的用户反馈翻译成中文,效果虽然可以,但其它不懂python的童鞋就没法使用了,所以搭了个web服务,让其他人可以通过网页访问查询.使用的是apac ...
随机推荐
- javaScript DOM编程经常使用的方法与属性
DOM是Document Object Model文档对象模型的缩写.依据W3C DOM规范,DOM是一种与浏览器,平台,语言无关的接口,使得你能够訪问页面其它的标准组件. Node接口的特性和方法 ...
- 谷歌 AI 中国中心成立,人工智能势不可挡?
昨日,谷歌在上海举办了一年一度的Google中国开发者大会.在本届大会上,谷歌云首席科学家李飞飞宣布了一个重磅消息,即在北京将成立谷歌AI中国中心.对于这个即将成立的AI中心谷歌寄予厚望,希望与中国本 ...
- Android学习Scroller(三)——控件平移划过屏幕 (Scroller简单使用)
MainActivity例如以下: package cc.cn; import android.os.Bundle; import android.view.View; import android. ...
- WPF Opacity 最小值多少会被击穿
粗略测试 这样也行.再小不懂咯(跟Double精度有关???) <WrapPanel.Background> <SolidColorBrush x:Name="opacit ...
- sparksql dataset
java /** *2.0之后使用sparksession即可,不需要再去创建sqlcontext *@author Tele * */ public class Demo { private sta ...
- 防爆搜索解决方案hdu1572下沙面(2)
#include<iostream> #include<map> #include<string> #include<cstring> #include ...
- node 设置自动启用定时任务控件 node-schedule
[转]Quartz中时间表达式的设置-----corn表达式 时间格式: <!-- s m h d m w(?) y(?) -->, 分别对应: 秒>分>小时>日&g ...
- message contains no documents code:13066 mongdb数据库报的错误
message contains no documents code:13066stackoverflow上面的回答是: What version of the C# driver are you ...
- Qt5.9 官方发布的新版本亮点的确不胜枚举(而且修复2000+ bugs)
作者:Summer Fang链接:https://www.zhihu.com/question/60486611/answer/177584284来源:知乎著作权归作者所有.商业转载请联系作者获得授权 ...
- zabbix 设备(自己的实践)
1. 下载源代码包 wget http://sourceforge.net/projects/zabbix/files/ 2. 解压 tar -zxvf zabbix-2.2.3.tar.gz 3. ...