Reiase's Blog

Happy coding

在Linux下链接win32 DLL
用Cython编写Wrapper(续)

用Cython编写Wrapper

Reiase posted @ 2009年10月14日 16:52 in 编程杂项 , 4024 阅读

最近尝试了下用cython编写python扩展。由于cython中可以同时调用C和Python代码,因此也是编写Wrapper的不错工具。手头一个程序需要Web化的远程界面,正好用Python和C混合编程来实现。嘿嘿,先放一个自己写的Demo上来

建立C程序

这里用一段简单的代码来模拟视频处理算法的一般情况:接受几个参数,在死循环里逐帧处理视频。

1
2
3
4
5
6
7
8
//a.h
typedef struct {
int param1;
int param2;
}a;

a* aAlloc(void);
void mainloop(a*);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//a.c
#include <stdio.h>
#include <stdlib.h>
#include "a.h"

a* aAlloc(void)
{
    return malloc(sizeof(a));
}

void mainloop(a* x)
{
    while (1) {
        printf("param1:%d\n",x->param1);
        printf("param2:%d\n",x->param2);
        system("sleep 1");
    }
}

编写wrapper程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#a_ext.pyx
cdef extern from "a.h":
    ctypedef struct c_a "a":
        int param1
        int param2

    cdef extern c_a *c_aAlloc "aAlloc" ()
    cdef extern void c_mainloop "mainloop"(c_a* x) nogil

cdef class a:
    cdef c_a *thisptr

    def __cinit__(self):
        self.thisptr = c_aAlloc()

    property param1:
        def __get__(self): return self.thisptr.param1
        def __set__(self,x): self.thisptr.param1 = x

    property param2:
        def __get__(self): return self.thisptr.param2
        def __set__(self,x): self.thisptr.param2 = x

    def mainloop(self):
        cdef c_a *a = self.thisptr
        with nogil:
            c_mainloop(a)

对Wrapper,逐行解释下:

  1. Wrapper程序分两部分,第一部分从a.h中导入相关函数,声明外部C函数的一般语法为:
    1
    cdef extern FILE *fopen (char* filename)

    除”cdef”关键字外,与C语法完全一致。这里要注意一点,Cython中同时有两种函数,C函数和Python函数。其中C函数可以从外部导入,也可以通过“cdef”声明来自己实现。用”def”声明的函数是Python函数。两种函数在Cython中能够相互调用,但是在C程序中只能调用Cython编写的模块中的C函数,在Python程序中只能调用其中的Python函数。Cython不区分C函数与Python函数,因此C函数与Python函数不能同名。我们从C头文件里导入了C函数”fopen”,就不能再声明一个Python函数”fopen”给Python程序调用。为了解决这种名字冲突问题,Cython在导入C函数时提供一种别名机制:

    1
    cdef extern FILE *c_fopen "fopen" (char* filename)

    上边代码表示将头文件里的C函数fopen导入为名为”c_fopen”的Cython C函数,之后我们可以为该函数提供一个名为”fopen”的Python接口:

    1
    2
    def fopen(filename):
        return c_fopen(filename)
  2. 主工作循环函数处,声明为”nogil”。Python使用全局解释器锁(GIL)来控制解释器资源的访问,同一时刻只能有一个解释器线程运行。因此,执行主循环的线程会阻塞其他线程。如果C扩展能够确保函数执行期间不访问解释器的资源,可以显式释放GIL,这就是”nogil”关键字的作用。
    1
    cdef extern void c_mainloop "mainloop"(c_a* x) nogil
  3. cython编写Wrapper最大的好处就是:Wrapper接口是Python风格的。Swig虽然自动化程度高一点,但接口都是同C接口保持一致的。我们使用Cython编写Wrapper时,可以为一个C风格的库,提供Python风格的面向对象的Wrapper:
    1
    2
    3
    4
    cdef class a:
            ...
        def mainloop(self):
            ...

    这里把mainloop封装为类a的成员函数。

构建系统

Python有自己的构建系统——Distutils。相比其他工具,Distutils专注与Python模块的构建与发布,并且能够完美支持多平台(Windows,Linux)。我们Demo使用如下构建脚本:

1
2
3
4
5
6
7
8
9
10
11
#setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [
        Extension("a", ["a_ext.pyx","a.c"])
        ]
)

在控制台中执行以下命令构建Wrapper:

1
python setup.py build

或者

1
python setup.py build_ext -i

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter