关闭

C++调用Python函数

标签: pythonc++stringimportincludedropdown
963人阅读 评论(0) 收藏 举报
分类:

From: http://www.flatws.cn/article/program/c/2010-08-24/9677.html

     Python代码在实现某些功能的时候非常方便,如果能够将Python代码与C++程序结合起来,那么一定会使Problem Solving方便许多(比如,游戏脚本系统)~

    从学完Python开始就一直想研究一下C++内嵌Python是怎么回事,可是每次都坚持不下来。。。今天上校内,偶然间看到同学求助,怎么实现输入表达式输出结果~我想到了Python的builtin函数eval,正好借这个机会研究一下怎么实现C++调用Python函数!

    假定您的系统中已经安装了Python,我使用的版本是2.6~

    1.配置IDE。

    我用的是VS2008,只需要将Python安装路径下的include文件夹和libs文件夹添加到C++目录中即可。步骤是:Tools -> Options -> Project & Solution -> VC++ Dir选择右上的Dropdown List,分别将include文件夹和libs文件夹添加到include和lib项中。

    2.编写Python脚本。

    为了完成自动计算值的功能,我写了如下的脚本(文件名是python_code.py):

def calculate(expression):
	try:        
		result = eval(expression)      
    except:
		print 'Eval error!'
		return None
	return result

    将这个文件与C++程序文件放到同一目录下,这样保证编译器可以根据相对路径找到。

    3.Python嵌入的方式 - 高级vs低级

    接下来进入C++内嵌Python函数的关键部分了。根据官方的帮助文档,有两个类型的Python调用,High Level与Low Level。High Level就是调用者不需要管理与Python相关的内存,只有3行代码即可,分别是Py_Initialize()来初始化解释器(注意,此时程序仍然是由C++的编译器进行编译),调用PyRun_SimpleString()其中参数是要执行的Python代码,如果是一个确定的Python文件,那么我们可以调用PyRun_SimpleFile()直接执行Python文件。最后调用Py_Finalize()方法结束解释器工作。

    使用High Level对程序员的要求非常少,基本就是会写Python代码就会在C++中调用Python的方法,但是弊端就是当前程序不能与Python的方法进行数据的交互,所以局限性非常大。

    所以,如果我们需要与Python程序进行交互,比如计算表达式的值,我们只能使用低级的方法了。

    4.Low Level程序编写步骤。

    说实话,以前几次当我想学习Python内嵌的时候都是因为看到为了进行Low Level调用而编写的冗长的C++代码,实在是太恐怖了。。可是今天静下来看一看其实只有4步:

    (1)导入要用到的模块:

     要导入模块其实很简单,首先我们需要提供模块的文件名,注意,跟Python中导入模块一样,这里的文件名不能包含".py"后缀,比如我的文件名是python_code.py,我在程序中只需要提供"python_code"这个字符串就可以了。这一部分代码如下(注意使用到的两个函数:PyString_FromString和PyImport_Import):

PyObject *pyFileName = PyString_FromString(filename.c_str());   
PyObject *pyMod = PyImport_Import(pyFileName);		// load the module

    注意,在C++中一切Python的object都是用PyObject*来代替。第一行语句将C++中的char数组转化为Python中的字符串,然后利用这个文件名导入模块!请注意函数是PyImport_Import,而不是别的。

    好了,模块导入之后我们需要验证模块导入是否成功,如果成功我们就可以进行第二步了,找到我们要调用的方法。

    (2)找到要调用的方法。

    基本原理是这样的,在C++中,如果我们有了模块,就可以获得模块对应的attr列表(dir(模块名)),然后我们就可以利用我们所需要的方法名找到我们所需要的那个attr。找到之后我们要判断时候找到成功,而且是否是可以调用的(callable)。这部分代码如下:

// load the function      
PyObject *pyFunc = PyObject_GetAttrString(pyMod, methodname.c_str());
if(pyFunc && PyCallable_Check(pyFunc))
{
	// talk later!!!
}  

    第一句话首先利用PyObject_GetAttrString找到我们所需的“方法”对象,然后检测对象时候有效,是否是Callable的,如果这两者检测都成功了,那么ok,进入下一步。

    (3)构造参数。

   方法有了,接下来就缺参数了。参数是以tuple的形式传递给方法的,所以我们需要一个PyObject*对象来充当一个Tuple,利用PyTuple_New方法即可。这个方法的参数是tuple内元素的个数,我认为可以理解为参数的个数。由于我的Python代码只有一个参数,所以这里我直接给1,其他情况可以根据参数的数量赋值。

    然后解释构建参数了,根据参数的不用类型,可以调用不同的方法。这里由于我在C++程序中的参数是字符串,在Python中的参数也是一个字符串,所以我使用PyString_FromString方法,其中Py是前缀,第一个String指的是在Python代码中我们需要一个String,也就是目的参数类型,而FromString顾名思义,就是C++中参数的类型。这个方法接受一个参数,也就是一个string,注意,这个string其实是char型数组。返回的结果是一个PyObject*。

    最后一步是要把构造的参数赋值给我们的Tuple,用PyTuple_SetItem方法就可以了。第一个参数是指向Tuple的PyObject指针,第二个参数是value在Tuple中的位置参数,从0开始,最后一个就是咱们的value了,这部分代码如下:

PyObject *pyParams = PyTuple_New(1);
PyObject *pyValue = PyString_FromString(expression.c_str());
PyTuple_SetItem(pyParams, 0, pyValue);

    (4)调用方法,取返回值。

    好了,大功即将告成~最后一步当然是调用方法了!利用PyObject_CallObject方法,第一个参数是指向函数的那个PyObject指针,第二个参数则是指向我们Tuple的那个PyObject指针。返回值仍然是PyObject指针对象。我们需要对它进行解析,将它转换成C++的类型。这部分代码如下:

// ok, call the function
pyValue = PyObject_CallObject(pyFunc, pyParams);
if(pyValue)
{
	*result = PyFloat_AsDouble(pyValue); // result is a double var
	return true;            
}    

     好了,到这里调用的全部过程就完成了,其实还不赖。最后,为了方便大家理解,我把整个代码贴了上来。

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    特别说明,如果您是用Python安装包的形式安装的Python,那么这个程序只能以Release方式运行!原因是安装包版Python在libs文件夹下只有Release版的lib文件,而debug模式需要Python26_d.lib,也就是debug模式的。如果您在运行时提示无法打开python26_d.lib文件,那么请换成Release模式,或者下载Python源码自己编译,这样就会生成_d的lib文件了!

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    由于我的水平实在有限,文中难免有错误,希望各位看到之后留言指正!谢谢各位!

#include <Python.h> // this is in <python-install-path>
#include <conio.h>
#include <iostream>
#include <string>

using namespace std;

bool calc(string filename, string methodname, string expression, double *result)
{
    PyObject *pyFileName = PyString_FromString(filename.c_str());
    PyObject *pyMod = PyImport_Import(pyFileName);	// load the module
    if (pyMod)										// if ok
    {
        PyObject *pyFunc = PyObject_GetAttrString(pyMod, methodname.c_str());	// load the function
        if (pyFunc && PyCallable_Check(pyFunc))
        {
            PyObject *pyParams = PyTuple_New(1);
            PyObject *pyValue = PyString_FromString(expression.c_str());
            PyTuple_SetItem(pyParams, 0, pyValue);
            
            pyValue = PyObject_CallObject(pyFunc, pyParams);	// ok, call the function
            if (pyValue)
            {
                *result = PyFloat_AsDouble(pyValue);
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
    return false;
}

int main(void)
{
    string filename = "python_code";	// filename is settled!
    string methodname = "calculate";	// method name is also settled!
    string expression = "";				// get user input!
    double result = 0.0;
    printf("\nPlease input the expression: ");
    getline(cin, expression);

	// first init
    Py_Initialize();
    if (calc(filename, methodname, expression, &result))
    {
        printf("\nResult is : %lf\n", result);
    }
    else
    {
        printf("\nError occurred...\n");
    }
    // last fini!
    Py_Finalize();
    getch();

    return 0;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2114805次
    • 积分:23992
    • 等级:
    • 排名:第289名
    • 原创:300篇
    • 转载:738篇
    • 译文:0篇
    • 评论:229条
    文章分类
    最新评论