Differences between revisions 1 and 2
Revision 1 as of 2004-09-06 05:06:04
Size: 6579
Editor: samhoo
Comment:
Revision 2 as of 2009-12-25 07:16:12
Size: 6579
Editor: localhost
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)>>

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语言模块添加“包裹”代码,分为以下四步:
    1. 加上Python.h头文件
    2. 给模块中的每个函数加上"PyObject * Moudle_func()" Python打包器,每个函数都有一个打包器。

    3. 把每个函数“注册”到"PyMethodDef ModuleMethods[]" 函数名/函数指针的映射表中。

    4. 加上"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语言的数据类型转换,上面我们演示的例子只演示了单个的、简单的数据类型,对于多个、复杂的数据类型,将随着项目的深入,不断补充。

资源


-- samhoo 2004-08-16 18:00:17

Roo/PythonUseCModelHowto (last edited 2009-12-25 07:16:12 by localhost)