⇤ ← Revision 1 as of 2004-09-06 05:06:04
Size: 6579
Comment:
|
← Revision 2 as of 2009-12-25 07:16:12 ⇥
Size: 6579
Comment: converted to 1.6 markup
|
Deletions are marked like this. | Additions are marked like this. |
Line 2: | Line 2: |
[[TableOfContents]] | <<TableOfContents>> |
Line 161: | Line 161: |
-- samhoo [[DateTime(2004-08-16T18:00:17Z)]] | -- samhoo <<DateTime(2004-08-16T18:00:17Z)>> |
Contents
Python如何使用C编写的模块
- 本文档通过对一个例子的演化,描述了如何用Python调用C语言编写的模块。例子?你猜对了,还是老套——helloworld!:编写一个简单功能的C语言程序helloworld.c,并实现python调用helloworld.c的函数。
纯手工打造
编写你的C语言模块
- helloworld.c
#include <stdio.h> #include <stdlib.h> #include <string.h> int fac(int n) { if (n < 2) return (1); return( (n) * fac(n - 1)); } int main() { printf("9! == %d\n", fac(9)); }
为了保证后续的步骤不受到前面步骤某些错误的影响,我们添加main这样的自我测试函数,以保证我们要包裹的函数已经正确书写。这是编译、运行的结果:gcc -o helloworld helloworld.c ./helloworld 9! == 362880
好了,下面我们就为fac函数添加“包裹”代码,让Python能访问它。
为模块添加Python包裹代码
- 添加了包裹代码的helloworld2.c
#include <stdio.h> #include <stdlib.h> #include <string.h> int fac(int n) { if (n < 2) return (1); return( (n) * fac(n - 1)); } int main() { printf("9! == %d\n", fac(9)); } /* 添加的包裹代码 */ #include "Python.h" static PyObject *hello_fac(PyObject *self, PyObject *args) { int num; if (!PyArg_ParseTuple(args, "i", &num)) return NULL; return (PyObject *)Py_BuildValue("i", fac(num)); } static PyMethodDef helloMethods[] = { {"fac", hello_fac, METH_VARARGS}, {NULL, NULL} }; void inithello() { Py_InitModule("hello", helloMethods); }
有点晕?其实很简单,一般为c语言模块添加“包裹”代码,分为以下四步:- 加上Python.h头文件
给模块中的每个函数加上"PyObject * Moudle_func()" Python打包器,每个函数都有一个打包器。
把每个函数“注册”到"PyMethodDef ModuleMethods[]" 函数名/函数指针的映射表中。
- 加上"void initModule()" 模块初始化函数。
而Python的调用次序正好相反:首先调用一次initMoulde得到函数名/函数指针映射表的函数,然后用这个函数解析函数名返回实际函数指针,最后调用经过“包裹”的函数,而包裹函数在调用相应函数前做Python->C的数据转换,然后调用c函数,最后进行C->Python的数据类型转换,返回给Python。
编译为Python可用的动态链接库
- 在2.0版本以前,Python使用一个Makefile.pre.in的脚本+固定格式的Setup文件来编译“包裹”过的C语言模块,从2.0版本以后,就采用了一种新的方式,编写一个py文件(一般命名为setup.py),然后执行python setup.py build就可以生成Python所需的动态链接文件.so
- setup.py文件
from distutils.core import setup, Extension^M moduleHello = Extension('hello', sources = ['helloworld2.c']) setup (name = 'hello', version = '1.0', description = 'This is a hello world package', ext_modules = [moduleHello])
生成的.so文件会放在build/lib.your_flatform_uname中,只需把它拷贝到Python可以找到的目录下即可,这里我们把它拷到当前目录下。fxh@~/simpleshm$python setup2.py build running build running build_ext building 'hello' extension cc -fno-strict-aliasing -DNDEBUG -O -pipe -D_THREAD_SAFE -DTHREAD_STACK_SIZE=0x100000 -fPIC -I/usr/local/include/python2.3 -c helloworld2.c -o build/temp.freebsd-4.10-STABLE-i386-2.3/helloworld2.o cc -shared -pthread build/temp.freebsd-4.10-STABLE-i386-2.3/helloworld2.o -o build/lib.freebsd-4.10-STABLE-i386-2.3/hello.so fxh@~/simpleshm$cp build/lib.freebsd-4.10-STABLE-i386-2.3/hello.so .
试试看吧
fxh@~/simpleshm$python Python 2.3.4 (#2, Aug 4 2004, 19:17:31) [GCC 2.95.4 20020320 [FreeBSD]] on freebsd4 Type "help", "copyright", "credits" or "license" for more information. >>> import hello >>> hello.fac(10) 3628800
更简单些 - 使用SWIG
- SWIG是一个代码生成工具:为脚本语言使用C/C++编写的模块生成“包裹”代码。它支持的脚本语言范围相当广泛:CHICKEN C# Guile Java Mzscheme Ocaml Perl PHP Pike Python Ruby Lisp S-Expressions Tcl XML(还有XML?没搞清楚啥概念),嘿嘿,Python位列其中,让我们来试试吧。使用SWIG的5个基本步骤:
- 根据你要包裹的文件:your_module.c编写SWIG模块定义文件:your_module.i
- 用SWIG对模块定义文件your_module.i进行预处理,生成your_moudle_wrap.c
- 编译your_module.c文件和your_module_wrap.c文件
- 链接上述生成的目标文件,生成动态链接库。
- 在python即可import该模块,并调用。 我们仍然使用上面的helloworld.c作为例子,这是我们编写的SWIG模块定义文件:helloworld.i
%module hello %{ %} extern int fac(int);
很简单,对吗?让我们继续——对helloworld.i进行预处理——SWIG为我们生成了一个可怕的、长达762行的程序:helloworld_wrap.c。fxh@~/simpleshm$swig -python helloworld.i fxh@~/simpleshm$wc helloworld_wrap.c 762 2372 20881 helloworld_wrap.c
好了,现在我们只需要编译、链接这两个文件:helloworld.c和helloworld_wrap.c就可以了:gcc -c -fpic helloworld.c helloworld_wrap.c -I/usr/local/include/python2.3 gcc -shared helloworld.o helloworld_wrap.o -o _hello.so
现在我们就可以用hello模块了:fxh@~/simpleshm$python Python 2.3.4 (#2, Aug 4 2004, 19:17:31) [GCC 2.95.4 20020320 [FreeBSD]] on freebsd4 Type "help", "copyright", "credits" or "license" for more information. >>> import hello >>> hello.fac(10) 3628800
更进一步 - 更复杂的数据类型
- 从上面的描述看来,“包裹”问题的关键在于Python和C语言的数据类型转换,上面我们演示的例子只演示了单个的、简单的数据类型,对于多个、复杂的数据类型,将随着项目的深入,不断补充。
资源
- 《Python核心编程》Ch20 Wesley J. Chun,机械工业出版社2001年。
Python中关于“包裹”C/C++的文档:http://www.python.org/doc/2.3.4/ext/ext.html
SWIG - http://www.swig.org (或 http://sourceforge.net/projects/swig )
-- samhoo 2004-08-16 18:00:17