python C扩展 - 漩涡鸣人

python

python C扩展

在C/C++中处理Python对象时,对引用计数进行正确的维护是一个关键问题,处理不好将很容易产生内存泄漏。Python的C语言接口提供了一些宏来对引用计数进行维护,最常见的是用 Py_INCREF()来增加使Python对象的引用计数增1,用Py_DECREF()来使Python对象的引用计数减1。

2.3 数据类型

Python定义了六种数据类型:整型、浮点型、字符串、元组、列表和字典,在使用C语言对Python进行功能扩展时,首先要了解如何在C和Python的数据类型间进行转化。

2.3.1 整型、浮点型和字符串

在Python的C语言扩展中要用到整型、浮点型和字符串这三种数据类型时相对比较简单,只需要知道如何生成和维护它们就可以了。下面的例子给出了如何在C语言中使用Python的这三种数据类型:

例2:typeifs.c

// build an integer

PyObject* pInt = Py_BuildValue("i", 2003);

assert(PyInt_Check(pInt));

int i = PyInt_AsLong(pInt);

Py_DECREF(pInt);

// build a float

PyObject* pFloat = Py_BuildValue("f", 3.14f);

assert(PyFloat_Check(pFloat));

float f = PyFloat_AsDouble(pFloat);

Py_DECREF(pFloat);

// build a string

PyObject* pString = Py_BuildValue("s", "Python");

assert(PyString_Check(pString);

int nLen = PyString_Size(pString);

char* s = PyString_AsString(pString);

Py_DECREF(pString);

 

2.3.2 元组

Python语言中的元组是一个长度固定的数组,当Python解释器调用C语言扩展中的方法时,所有非关键字(non-keyword)参数都以元组方式进行传递。下面的例子示范了如何在C语言中使用Python的元组类型:

例3:typetuple.c

// create the tuple

PyObject* pTuple = PyTuple_New(3);

assert(PyTuple_Check(pTuple));

assert(PyTuple_Size(pTuple) == 3);

// set the item

PyTuple_SetItem(pTuple, 0, Py_BuildValue("i", 2003));

PyTuple_SetItem(pTuple, 1, Py_BuildValue("f", 3.14f));

PyTuple_SetItem(pTuple, 2, Py_BuildValue("s", "Python"));

// parse tuple items

int i;

float f;

char *s;

if (!PyArg_ParseTuple(pTuple, "ifs", &i, &f, &s))

PyErr_SetString(PyExc_TypeError, "invalid parameter");

// cleanup

Py_DECREF(pTuple);

 

2.3.3 列表

Python语言中的列表是一个长度可变的数组,列表比元组更为灵活,使用列表可以对其存储的Python对象进行随机访问。下面的例子示范了如何在C语言中使用Python的列表类型:

例4:typelist.c

// create the list

PyObject* pList = PyList_New(3); // new reference

assert(PyList_Check(pList));

// set some initial values

for(int i = 0; i < 3; ++i)

PyList_SetItem(pList, i, Py_BuildValue("i", i));

// insert an item

PyList_Insert(pList, 2, Py_BuildValue("s", "inserted"));

// append an item

PyList_Append(pList, Py_BuildValue("s", "appended"));

// sort the list

PyList_Sort(pList);

// reverse the list

PyList_Reverse(pList);

// fetch and manipulate a list slice

PyObject* pSlice = PyList_GetSlice(pList, 2, 4); // new reference

for(int j = 0; j < PyList_Size(pSlice); ++j) {

PyObject *pValue = PyList_GetItem(pList, j);

assert(pValue);

}

Py_DECREF(pSlice);

// cleanup

Py_DECREF(pList);

 

2.3.4 字典

Python语言中的字典是一个根据关键字进行访问的数据类型。下面的例子示范了如何在C语言中使用Python的字典类型:

例5:typedic.c

// create the dictionary

PyObject* pDict = PyDict_New(); // new reference

assert(PyDict_Check(pDict));

// add a few named values

PyDict_SetItemString(pDict, "first",

Py_BuildValue("i", 2003));

PyDict_SetItemString(pDict, "second",

Py_BuildValue("f", 3.14f));

// enumerate all named values

PyObject* pKeys = PyDict_Keys(); // new reference

for(int i = 0; i < PyList_Size(pKeys); ++i) {

PyObject *pKey = PyList_GetItem(pKeys, i);

PyObject *pValue = PyDict_GetItem(pDict, pKey);

assert(pValue);

}

Py_DECREF(pKeys);

// remove a named value

PyDict_DelItemString(pDict, "second");

// cleanup

Py_DECREF(pDict);




回页首


三、Python的C语言扩展

3.1 模块封装

在了解了Python的C语言接口后,就可以利用Python解释器提供的这些接口来编写Python的C语言扩展,假设有如下一个C语言函数:

例6:example.c

int fact(int n)

{

if (n <= 1)

return 1;

else

return n * fact(n - 1);

}

 

该函数的功能是计算某个给定自然数的阶乘,如果想在Python解释器中调用该函数,则应该首先将其实现为Python中的一个模块,这需要编写相应的封装接口,如下所示:

例7: wrap.c

#include <Python.h>

PyObject* wrap_fact(PyObject* self, PyObject* args)

{

int n, result;

if (! PyArg_ParseTuple(args, "i:fact", &n))

return NULL;

result = fact(n);

return Py_BuildValue("i", result);

}

static PyMethodDef exampleMethods[] =

{

{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},

{NULL, NULL}

};

void initexample()

{

PyObject* m;

m = Py_InitModule("example", exampleMethods);

}

 

一个典型的Python扩展模块至少应该包含三个部分:导出函数、方法列表和初始化函数。

3.2 导出函数

要在Python解释器中使用C语言中的某个函数,首先要为其编写相应的导出函数,上述例子中的导出函数为wrap_fact。在Python的C语言扩展中,所有的导出函数都具有相同的函数原型:

PyObject* method(PyObject* self, PyObject* args);

该函数是Python解释器和C函数进行交互的接口,带有两个参数:self和args。参数self只在C函数被实现为内联方法(built-in method)时才被用到,通常该参数的值为空(NULL)。参数args中包含了Python解释器要传递给C函数的所有参数,通常使用Python的 C语言扩展接口提供的函数PyArg_ParseTuple()来获得这些参数值。

所有的导出函数都返回一个PyObject指针,如果对应的C函数没有真正的返回值(即返回值类型为void),则应返回一个全局的None对象(Py_None),并将其引用计数增1,如下所示:

PyObject* method(PyObject *self, PyObject *args) 

{

Py_INCREF(Py_None);

return Py_None;

}

 

3.3 方法列表

方法列表中给出了所有可以被Python解释器使用的方法,上述例子对应的方法列表为:

static PyMethodDef exampleMethods[] = 

{

{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},

{NULL, NULL}

};

 

方法列表中的每项由四个部分组成:方法名、导出函数、参数传递方式和方法描述。方法名是从Python解释器中调用该方法时所使用的名字。参数传递方式则规定了Python向C函数传递参数的具体形式,可选的两种方式是METH_VARARGS和METH_KEYWORDS,其中METH_VARARGS是参数传递的标准形式,它通过Python的元组在Python解释器和C函数之间传递参数,若采用METH_KEYWORD方式,则Python解释器和C函数之间将通过Python的字典类型在两者之间进行参数传递。

3.4 初始化函数

所有的Python扩展模块都必须要有一个初始化函数,以便Python解释器能够对模块进行正确的初始化。Python解释器规定所有的初始化函数的函数名都必须以init开头,并加上模块的名字。对于模块example来说,则相应的初始化函数为:

void initexample() 

{

PyObject* m;

m = Py_InitModule("example", exampleMethods);

}

 

当Python解释器需要导入该模块时,将根据该模块的名称查找相应的初始化函数,一旦找到则调用该函数进行相应的初始化工作,初始化函数则通过调用Python的C语言扩展接口所提供的函数 Py_InitModule(),来向Python解释器注册该模块中所有可以用到的方法。

3.5 编译链接

要在Python解释器中使用C语言编写的扩展模块,必须将其编译成动态链接库的形式。下面以RedHat Linux 8.0为例,介绍如何将C编写的Python扩展模块编译成动态链接库:

[xiaowp@gary code]$ gcc -fpic -c -I/usr/include/python2.2 \

-I /usr/lib/python2.2/config \

example.c wrapper.c

[xiaowp@gary code]$ gcc -shared -o example.so example.o wrapper.o

 

3.6 引入Python解释器

当生成Python扩展模块的动态链接库后,就可以在Python解释器中使用该扩展模块了,与Python自带的模块一样,扩展模块也是通过import命令引入后再使用的,如下所示:

[xiaowp@gary code]$ python

Python 2.2.1 (#1, Aug 30 2002, 12:15:30)

[GCC 3.2 20020822 (Red Hat Linux Rawhide 3.2-4)] on linux2

Type "help", "copyright", "credits" or "license" for more information.

>>> import example

>>> example.fact(4)

24

>>>

 

  >>import foo

    >>foo.bar( \'11111\' )

    5

    如果觉得自己已经了解了如何编译成python可以使用的东西,那么就不浪费你的宝贵的时间看下去了,而是给我投以鄙视的目光:这么简单的问题,还那来说.

    下面给出例子:在我的linux板块下有<Linux动态链接库编程入门>.

 

  /*foomain.c*/

#include <python2.4/Python.h>
/* Define the method table. */
static PyObject *foo_bar(PyObject *self, PyObject *args);
static PyMethodDef FooMethods[] = {
   {"bar",  foo_bar, METH_VARARGS},
   {NULL, NULL}
};
/* Here\'s the initialization function.  We don\'t need to do anything
 *    for our own needs, but Python needs that method table. */
void initfoo()
{
   (void) Py_InitModule("foo", FooMethods);
}
/* Finally, let\'s do something ... involved ... as an example function. */
static PyObject *foo_bar(PyObject *self, PyObject *args)
{
   char *string;
   int   len;
   if (!PyArg_ParseTuple(args, "s", &string))
       return NULL;
   len = strlen(string);
   return Py_BuildValue("i", len);
}

 

在linux下的运行: gcc -fPIC -shared -o foo.so foomain.c 这一段就ok了.

当前目录下会出现"foo.so"文件.

 

以上是 python C扩展 - 漩涡鸣人 的全部内容, 来源链接: utcz.com/z/386516.html

回到顶部