Python 的 C/C++ 扩展
By phidoit@gmail.com
可扩展性是 Python 的一大特色,一方面,由于 Python 是解释执行的,这导致运行速度会比编译型语言慢,因此可以通过使用 C/C++ 重写核心部分代码以解决性能上的瓶颈 ( 程序 90% 的时间再运行 10% 的代码 ) ;另一方面,可以通过扩展,达到添加整合一些额外的功能以及保持专有源代码的目的。在本文接下来的部分中我们将讨论如何编写 C/C++ 扩展代码,并使用它们的功能。
我们要建立的是一个可以在 Python 内运行的 C/C++ 模块,因此需要解决如何使 C 代码和 Python 代码能够进行交互以及数据共享。扩展是通过为 C 代码编写包装函数 ( 类似适配器 ) 实现双向交互和数据共享的。
一.一般的包装模式
每一个包装函数主要做三件事:
1. 把输入的 Python 对象转换为 C/C++ 对象;
2. 调用 C/C++ 函数;
3. 转换 C/C++ 函数处理的输出结果为 Python 对象,并返回;
先用一个简单样例描述模块大体的过程:
把上面的代码编译,生成 wrap.pyd 。
Visual Studio 2005 编译方法参考:
http://blog.csdn.net/solo_lxy/archive/2007/07/20/1700515.aspx
启动控制台切换到相应的工程目录,即可测试生成的模块:
(PS: 似乎只有 Release 模式生成的模块才能正常运行 )
相关说明
每个包装函数都有如下形式:
PyObject * wrap_function(PyObject *, PyObject * args)
函数第一个参数,有特殊用途,通常选择忽略。第二个参数是一个PyTuple(PyObject的子类型,和Python中的Tuple对应),是调用时Python传入的参数。
函数 PyArg_ParseTuple 把 Python 对象转换为 C 的数据类型,其声明如下 :
int PyArg_ParseTuple(PyObject* args, char* format, ...);
参数 args 必须是一个 tuple 对象,包含传递过来的参数, format 参数必须是格式化字符串。剩余参数是各个变量的地址,类型要与格式化字符串对应。如:
int arg1, arg2;
PyArg_ParseTuple(args, "ii", &arg1, &arg2);
函数 Py_BuildValue 可以说是 PyArg_ParseTuple 的逆过程,它把 C 的数据类型包装为 Python 对象。
return (PyObject*) Py_BuildValue("i", result);
把调用 C 函数的结果 result 包装为 Python 的 int 对象,并返回。
static PyMethodDef wrap_methods[] ={
{"add", wrap_add , METH_VARARGS},
{NULL, NULL}
};
这个数组包含多个数组,其中的每个数组都包含了一个函数的信息,以便解释器能够导入并调用它们,最后一个 NULL 数组表示列表的结束。 METH_VARARGS 常量表示参数以元组形式传入。
PyMODINIT_FUNC initwrap (void)
{
Py_InitModule("wrap ", wrap_methods);
}
模块初始化函数 void initModuleName(), 这部分代码在模块被导入的时候被解释器调用。这样所有的包装就已经完成了。
二. C++ 类的包装
C++
类的包装和
C
函数的包装大同小异,因为对类的包装是对函数的包装,所以仍需要用
Python
代码对扩展模块进行包装,才可以像类一样的使用。
这样
C++
类的包装也完成了。
三.
C/C++
中创建
Python list
创建其它 Python 对象也类似 list 的创建,返回给 解释器的都是一个对象指针 。 C/C++ 对 Python 对象的解析差不多是创建时的逆过程。具体的对象模型及 API 可以查阅相关参考文档。
Python v2.6.2 documentation » Python/C API Reference Manual » Concrete Objects Layer
如对本文有疑问,请提交到交流论坛,广大热心网友会为你解答!! 点击进入论坛