『Python CoolBook』C擴展庫_其四_結構體操做與Capsule

點擊進入項目git

1、Python生成C語言結構體

C語言中的結構體傳給Python時會被封裝爲膠囊(Capsule),程序員

咱們想要一個以下結構體進行運算,則須要Python傳入x、y兩個浮點數,github

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():從結構體建立膠囊ui

PyCapsule_GetPointer():提取膠囊中的指針,使用 PyCapsule_GetPointer() 函數並指定名稱。 若是提供的名稱和膠囊不匹配或其餘錯誤出現,那麼就會拋出異常並返回NULL。實際上就是從膠囊轉換回結構體spa

效果以下,設計

>>> 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底下的包裝的更改。 例如,若是你決定使用另一個膠囊了,那麼只須要更改這兩個函數便可。3d

對於膠囊對象一個難點在於垃圾回收和內存管理。 PyPoint_FromPoint() 函數接受一個 must_free 參數, 用來指定當膠囊被銷燬時底層Point * 結構體是否應該被回收。 在某些C代碼中,歸屬問題一般很難被處理(好比一個Point結構體被嵌入到一個被單獨管理的大結構體中)。 程序員能夠使用 extra 參數來控制,而不是單方面的決定垃圾回收。 要注意的是和現有膠囊有關的析構器能使用 PyCapsule_SetDestructor() 函數來更改。指針

2、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
相關文章
相關標籤/搜索