c和c++调用Python

一、使用python提供给C/C++的API

1. 包含头文件

​ #include "Python.h"
​ 注:因为python可能定义一些影响某些系统标准头的预处理器定义,所以必需在包含标准头文件(<stdio.h>, <string.h>, <errno.h>, <limits.h>, <assert.h> and <stdlib.h>)之前包含Python.h。

2.API介绍

以下是一些API的介绍:
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()来构建。

二、C++ 向 Python 传递参数

Python 的参数实际上是元组,因此传参实际上就是构造一个合适的元组。

常用的有两种方法:

1.使用 PyTuple_New 创建元组, PyTuple_SetItem 设置元组值  

PyObject* args = PyTuple_New(3);
PyObject* arg1 = Py_BuildValue("i", 100); // 整数参数
PyObject* arg2 = Py_BuildValue("f", 3.14); // 浮点数参数
PyObject* arg3 = Py_BuildValue("s", "hello"); // 字符串参数
PyTuple_SetItem(args, 0, arg1);
PyTuple_SetItem(args, 1, arg2);
PyTuple_SetItem(args, 2, arg3);



2.直接使用Py_BuildValue构造元组

PyObject* args = Py_BuildValue("(ifs)", 100, 3.14, "hello");
PyObject* args = Py_BuildValue("()"); // 无参函数

三、PyArg_ParseTuple函数

作用:此函数其实相当于sscanf(str,format,…),是Py_BuildValue的逆过程,这个函数将PyObject参数转换成C/C++数据类型,传递的是指针,但这个函数与Py_BuildValue有点不同,这个函数只能解析Tuple元组,而Py_BuildValue函数可以生成元组,列表,字典等。
原型:PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *args, const char *format,...)

Args:一般为Python程序返回的元组。

Foramt:与Py_BulidValue类型,就不在累述咯

四、 Py_BuildValue()函数

PyObject* Py_BuildValue(char* format, …)

参数解释:format及转换格式,类似与C语言中%d,%f,后面的不定参数对应前面的格式,具体格式如下:
"s"(string) [char *] :将C字符串转换成Python对象,如果C字符串为空,返回NONE。

     "s#"(string) [char *, int] :将C字符串和它的长度转换成Python对象,如果C字符串为空指针,长度忽略,返回NONE。

     "z"(string or None) [char *] :作用同"s"。

    "z#" (stringor None) [char *, int] :作用同"s#"。

    "i"(integer) [int] :将一个C类型的int转换成Python int对象。

     "b"(integer) [char] :作用同"i"。

     "h"(integer) [short int] :作用同"i"。

     "l"(integer) [long int] :将C类型的long转换成Pyhon中的int对象。

     "c"(string of length 1) [char] :将C类型的char转换成长度为1的Python字符串对象。

     "d"(float) [double] :将C类型的double转换成python中的浮点型对象。

     "f"(float) [float] :作用同"d"。

     "O&"(object) [converter, anything] :将任何数据类型通过转换函数转换成Python对象,这些数据作为转换函数的参数被调用并且返回一个新的Python对象,如果发生错误返回NULL。

     "(items)"(tuple) [matching-items] :将一系列的C值转换成Python元组。

     "[items]"(list) [matching-items] :将一系列的C值转换成Python列表。

     "{items}"(dictionary) [matching-items] :将一系类的C值转换成Python的字典,每一对连续的C值将转换成一个键值对。


举例:

     后面为PyObject的返回值

	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))

五、元组操作函数:

因为程序之间传递的参数,大多数为Tuple类型,所以有专门的函数来操作元组:

PyAPI_FUNC(PyObject *)PyTuple_New(Py_ssize_t size);

解释:新建一个参数列表(调试了下,发现其实是用链表实现的),size列表为长度的宽度

PyAPI_FUNC(Py_ssize_t)PyTuple_Size(PyObject *);

解释:获取该列表的大小

PyAPI_FUNC(PyObject *)PyTuple_GetItem(PyObject *, Py_ssize_t);

解释:获取该列表某位置的值

PyAPI_FUNC(int) PyTuple_SetItem(PyObject *,Py_ssize_t, PyObject *);

解释:设置该列表此位置的值。如PyTuple_SetItem(pyParams,1,Py_BuildValue("i",2));设置第2个位置的值为2的整数。

方法一:主流方法将python程序编程文本形式的动态链接库,在c/c++程序中调用其中定义的函数

1. 链接到Python调用库

Python 安装目录下已经包含头文件( include 目录)和库文件 ( Windows 下为 python27.lib)。
使用之前需要链接到此库。

2. 直接调用 Python 语句

#include "python/Python.h"

int main()
{
    Py_Initialize();    ## 初始化

    PyRun_SimpleString("print 'hello'");

    Py_Finalize();      ## 释放资源
}

3. 加载 Python 模块并调用函数

​ test.py :

def test_add(a, b):
    print 'add ', a, ' and ', b
    return a+b


#include "python/Python.h"
#include <iostream>
using namespace std;

int main()
{
    Py_Initialize();    // 初始化

    // 将Python工作路径切换到待调用模块所在目录,一定要保证路径名的正确性
    string path = "~/test";
    string chdir_cmd = string("sys.path.append(\"") + path + "\")";
    const char* cstr_cmd = chdir_cmd.c_str();
    PyRun_SimpleString("import sys");
    PyRun_SimpleString(cstr_cmd);

    // 加载模块
    PyObject* moduleName = PyString_FromString("test"); //模块名,不是文件名
    PyObject* pModule = PyImport_Import(moduleName);
    if (!pModule) // 加载模块失败
    {
        cout << "[ERROR] Python get module failed." << endl;
        return 0;
    }
    cout << "[INFO] Python get module succeed." << endl;

    // 加载函数
    PyObject* pv = PyObject_GetAttrString(pModule, "test_add");
    if (!pv || !PyCallable_Check(pv))  // 验证是否加载成功
    {
        cout << "[ERROR] Can't find funftion (test_add)" << endl;
        return 0;
    }
    cout << "[INFO] Get function (test_add) succeed." << endl;

    // 设置参数
    PyObject* args = PyTuple_New(2);   // 2个参数
    PyObject* arg1 = PyInt_FromLong(4);    // 参数一设为4
    PyObject* arg2 = PyInt_FromLong(3);    // 参数二设为3
    PyTuple_SetItem(args, 0, arg1);
    PyTuple_SetItem(args, 1, arg2);

    // 调用函数
    PyObject* pRet = PyObject_CallObject(pv, args);

    // 获取参数
    if (pRet)  // 验证是否调用成功
    {
        long result = PyInt_AsLong(pRet);
        cout << "result:" << result;
    }

    Py_Finalize();      ## 释放资源

    return 0;
}

方法二:完整调用python脚本文件及参数的方法。

以下是测试用的python脚本文件,功能是输出命令行参数:sample.py

# !/usr/bin/env python2.6
import sys
def test():
    for arg in sys.argv:
        print arg

if __name__=='__main__':
    test()

以下是测试用的c程序文件:test.c

#include "python2.6/Python.h"
int main()
{
    //第一步:初始化Python
    //在调用Python的提供的给C的API之前,通过执行初始化
    //来添加Python的内建模块、__main__、sys等
    Py_Initialize();

    //检查初始化是否完成
    if (!Py_IsInitialized())
    {
        return -1;
    }

    //第二步:导入sys模块
    PyRun_SimpleString("import sys");

    //第三步:导入执行脚本时的命令行参数,如:./sample.py int argc = 2;   
	char *argv[2];  
	argv[0] = "arg1";   
	argv[1] = "arg2";
	PySys_SetArgv(argc, argv);

    //第四步:执行调用脚本文件命令,注意文件的路径
    if (PyRun_SimpleString("execfile('./sample.py')") == NULL)
    {
        return -1;
    }

    //第五步:关闭Python解释器
    Py_Finalize();
    return 0;
}

编译指令:

g++ -g -W -o test test.cpp -I /usr/include/ -L /usr/lib64/ -l python2.
执行指令:./test
输出:

arg1
arg2

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×