用Cython编写Wrapper
最近尝试了下用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 |
编写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,逐行解释下:
- Wrapper程序分两部分,第一部分从a.h中导入相关函数,声明外部C函数的一般语法为:
1cdef 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函数时提供一种别名机制:
1cdef extern FILE *c_fopen "fopen" (char* filename)上边代码表示将头文件里的C函数fopen导入为名为”c_fopen”的Cython C函数,之后我们可以为该函数提供一个名为”fopen”的Python接口:
1
2def fopen(filename):
return c_fopen(filename) - 主工作循环函数处,声明为”nogil”。Python使用全局解释器锁(GIL)来控制解释器资源的访问,同一时刻只能有一个解释器线程运行。因此,执行主循环的线程会阻塞其他线程。如果C扩展能够确保函数执行期间不访问解释器的资源,可以显式释放GIL,这就是”nogil”关键字的作用。
1cdef extern void c_mainloop "mainloop"(c_a* x) nogil
- cython编写Wrapper最大的好处就是:Wrapper接口是Python风格的。Swig虽然自动化程度高一点,但接口都是同C接口保持一致的。我们使用Cython编写Wrapper时,可以为一个C风格的库,提供Python风格的面向对象的Wrapper:
1
2
3
4cdef 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 |