『Python CoolBook』C扩展库_其四_结构体操作与Capsule
一、Python生成C语言结构体
C语言中的结构体传给Python时会被封装为胶囊(Capsule),
我们想要一个如下结构体进行运算,则需要Python传入x、y两个浮点数,
typedef struct Point { double x,y; } Point;
然后对这两个浮点数解析后生成C中Point的结构体,如下,
/* Create a new Point object */ static PyObject *py_Point(PyObject *self, PyObject *args) { Point *p; double x,y; if (!PyArg_ParseTuple(args,"dd",&x,&y)) { return NULL; } p = (Point *) malloc(sizeof(Point)); p->x = x; p->y = y; return PyPoint_FromPoint(p, 1); }
上面最后一句将使用C中的结构体构建Python胶囊对象并返回给Python,
/* Destructor function for points */ static void del_Point(PyObject *obj) { free(PyCapsule_GetPointer(obj,"Point")); } static PyObject *PyPoint_FromPoint(Point *p, int must_free) { /* 胶囊和C指针类似。在内部,它们获取一个通用指针和一个名称,可以使用 PyCapsule_New() 函数很容易的被创建。 另外,一个可选的析构函数能被 绑定到胶囊上,用来在胶囊对象被垃圾回收时释放底层的内存*/ return PyCapsule_New(p, "Point", must_free ? del_Point : NULL); }
PyCapsule_New():从结构体创建胶囊
PyCapsule_GetPointer()
:提取胶囊中的指针,使用 PyCapsule_GetPointer()
函数并指定名称。 如果提供的名称和胶囊不匹配或其他错误出现,那么就会抛出异常并返回NULL。实际上就是从胶囊转换回结构体
效果如下,
>>> import sample
>>> p1 = sample.Point(2,3)
>>> p2 = sample.Point(4,5)
>>> p1
<capsule object "Point" at 0x1004ea330>
>>> p2
<capsule object "Point" at 0x1005d1db0>
总结
本节中,一对工具函数—— PyPoint_FromPoint()
和 PyPoint_AsPoint()
被用来创建和从胶囊对象中提取Point实例。 在任何扩展函数中,我们会使用这些函数而不是直接使用胶囊对象。 这种设计使得我们可以很容易的应对将来对Point底下的包装的更改。 例如,如果你决定使用另外一个胶囊了,那么只需要更改这两个函数即可。
对于胶囊对象一个难点在于垃圾回收和内存管理。 PyPoint_FromPoint()
函数接受一个 must_free
参数, 用来指定当胶囊被销毁时底层Point * 结构体是否应该被回收。 在某些C代码中,归属问题通常很难被处理(比如一个Point结构体被嵌入到一个被单独管理的大结构体中)。 程序员可以使用 extra
参数来控制,而不是单方面的决定垃圾回收。 要注意的是和现有胶囊有关的析构器能使用 PyCapsule_SetDestructor()
函数来更改。
二、Python中的C结构体传入C语言进行运算
/* Utility functions */ static Point *PyPoint_AsPoint(PyObject *obj) { return (Point *) PyCapsule_GetPointer(obj, "Point"); } static PyObject *py_distance(PyObject *self, PyObject *args) { Point *p1, *p2; PyObject *py_p1, *py_p2; double result; if (!PyArg_ParseTuple(args,"OO",&py_p1, &py_p2)) { return NULL; } if (!(p1 = PyPoint_AsPoint(py_p1))) { return NULL; } if (!(p2 = PyPoint_AsPoint(py_p2))) { return NULL; } result = distance(p1,p2); return Py_BuildValue("d", result); }
将两个结构体转为C指针存储后,分别使用PyCapsule_GetPointer()
提取指针信息转换为结构体,计算后返回。
>>> sample.distance(p1,p2)
2.8284271247461903