[Python自动化]使用C来扩展Python

一、需求背景

Python 几乎能解决你所遇到的所有问题,但 Python 常被人提及的问题就是速度问题,这时如果想提升 Python 的速度,基本都会使用 C/C++ 来扩展 Python 接口,这种方法仅仅是提升 Python 速度的诸多方法中的一种而已。

同时,对于一些使用 Python 来解决的问题比较棘手时,也可以考虑使用 C/C++ 来扩展 Python 接口,这样在调用的时候,就会减少一些 Python 带来的麻烦。

基于不同的平台,使用 C/C++ 扩展 Python 接口时,产生的文件也不一样(*.so 或 *.dll),然后将其改为合适的后缀,本文使用 Win7 和 VS2013 来编写 Python 扩展。

二、准备工作

使用 VS2013 新建一个工程,并做如下操作:

设置 Configuration Type 属性为 Dynamic Library(.dll)

选择 Release 模式(因为没有 python27_d.lib 文件)

添加 python 头文件路径(默认为 C:\Python27\include\)

添加 python 库文件路径(默认为 C:\Python27\libs\)

可以将 include 和 libs 拷贝放到一个文件夹下,然后放在工程目录

三、Python 对象

Python 对象在 Python 解析器中都为PyObject。

而在 C/C++ 中只能声明为 PyObject* 类型的 python 对象,然后使用该对象对应的初始化函数初始化。

如:PyTuple_New,PyList_New,PyDict_New,Py_BuildValue等。

如何构建一个{‘key1’: {‘key2’: [‘valuex’, ‘valuey’]}}对象?

PyObject* pObj1 = PyDict_New();PyObject* pObj2 = PyDict_New();PyObject* pObj3 = PyList_New(2);PyList_SetItem(pObj3, 0, Py_BuildValue("s", "valuex"));PyList_SetItem(pObj3, 1, Py_BuildValue("s", "valuey"));PyDict_SetItem(pObj2, "key2", pObj3);PyDict_SetItem(pObj1, "key1", pObj2);四、Python 内存管理

Python 对象管理使用引用计数技术,从而实现自动垃圾回收机制。

提供两个宏Py_INCREF和Py_DECREF来管理引用计数。

前文中申请的 pObj1/pObj2/pObj3 对象如果需要释放,应该如何处理?

不能直接free/delete,必须使用Py_DECREF(pObj1),然后pObj1 = NULL即可。

五、编写 Python 扩展编写 Python 扩展主要包括三步:

实现接口函数

定义方法列表

实现初始化函数

示例功能

实现一个 add 函数,接受三个整形参数,求其和并返回。

1. 实现接口函数

先看代码,然后进行说明。

static PyObject* add(PyObject* self, PyObject* args){ int x = 0; int y = 0; int z = 0; int i = 0; if (!PyArg_ParseTuple(args, "iii", &x, &y, &z)) {return NULL; } i = x + y + z; return Py_BuildValue("i", i);}

接口函数必须是static函数,同时必须返会PyObject*类型。

接口函数的第一个参数是PyObject* self,该参数是 Python 内部使用的,暂时不深究。

接口函数的第二个参数是PyObject* args,该参数是一个参数列表,把所有参数都整合为一个string,因此需要从这个string参数里解析参数,函数PyArg_ParseTuple就是来完成这项任务的,该函数的第一个参数agrs就是需要转换的参数,第二个参数iii是格式符号,第三个参数及后面的参数就是提取出来的参数存放的真正位置,这些参数必须传递其地址。对于add函数,会提取三个参数,分别是x,y,z。其中,格式串"iii"代表三个int, 类型为string则是"s",如果有三个string则格式符号为"ss"。

接口函数必须返回结果,其类型可以是 C/C++ 类型或是自定义类型,但是最后都必须把其转换成PyObject*, 让 Python 认识这个对象,这个类型转换使用函数Py_BuildValue来完成,该函数是函数PyArg_ParseTuple的逆过程。它的第一个参数和PyArg_ParseTuple的第二个参数一样,都是格式化符号,第二个参数是需要转换的参数,函数Py_BuildValue会把所有的返回只组装成一个 tuple 给Python 所用。

2. 定义方法列表

同样,还是先看示例代码,再进行解释说明。

static PyMethodDef addMethods[] ={ {"add", (PyCFunction)add, METH_VARARGS, "add(int1, int2, int3)"}, {NULL, NULL, 0, NULL}};

PyMethodDef是一个 C 结构体,用来完成一个映射,需要把扩展的函数都映射到这个表里。

表的第一个字段是 Python 真正认识的,也就是 Python 里的可以使用的方法名字。

那段雨骤风狂。人生之旅本就是风雨兼程,是要说曾经拥有,

[Python自动化]使用C来扩展Python

相关文章:

你感兴趣的文章:

标签云: