15.10 用Cython包装C代码?
问题?
你想使用Cython来创建一个Python扩展模块,用来包装某个已存在的C函数库。
解决方案?
使用Cython构建一个扩展模块看上去很手写扩展有些类似,
因为你需要创建很多包装函数。不过,跟前面不同的是,你不需要在C语言中做这些——代码看上去更像是Python。
作为准备,假设本章介绍部分的示例代码已经被编译到某个叫 libsample
的C函数库中了。
首先创建一个名叫 csample.pxd
的文件,如下所示:
# csample.pxd
#
# Declarations of "external" C functions and structures
cdef extern from "sample.h":
int gcd(int, int)
bint in_mandel(double, double, int)
int divide(int, int, int *)
double avg(double *, int) nogil
ctypedef struct Point:
double x
double y
double distance(Point *, Point *)
这个文件在Cython中的作用就跟C的头文件一样。
初始声明 cdef extern from "sample.h"
指定了所学的C头文件。
接下来的声明都是来自于那个头文件。文件名是 csample.pxd
,而不是 sample.pxd
——这点很重要。
下一步,创建一个名为 sample.pyx
的问题。
该文件会定义包装器,用来桥接Python解释器到 csample.pxd
中声明的C代码。
# sample.pyx
# Import the low-level C declarations
cimport csample
# Import some functionality from Python and the C stdlib
from cpython.pycapsule cimport *
from libc.stdlib cimport malloc, free
# Wrappers
def gcd(unsigned int x, unsigned int y):
return csample.gcd(x, y)
def in_mandel(x, y, unsigned int n):
return csample.in_mandel(x, y, n)
def divide(x, y):
cdef int rem
quot = csample.divide(x, y, &rem)
return quot, rem
def avg(double[:] a):
cdef:
int sz
double result
sz = a.size
with nogil:
result = csample.avg(<double *> &a[0], sz)
return result
# Destructor for cleaning up Point objects
cdef del_Point(object obj):
pt = <csample.Point *> PyCapsule_GetPointer(obj,"Point")
free(<void *> pt)
# Create a Point object and return as a capsule
def Point(double x,double y):
cdef csample.Point *p
p = <csample.Point *> malloc(sizeof(csample.Point))
if p == NULL:
raise MemoryError("No memory to make a Point")
p.x = x
p.y = y
return PyCapsule_New(<void *>p,"Point",<PyCapsule_Destructor>del_Point)
def distance(p1, p2):
pt1 = <csample.Point *> PyCapsule_GetPointer(p1,"Point")
pt2 = <