『Python CoolBook』C擴展庫_其三_簡單數組操做

點擊進入項目python

這裏的數組要點在於:git

  • 數組結構,array.array或者numpy.array
  • 本篇的數組僅限一維,不過基礎的C數組也是一維

1、分塊講解

源函數

/* Average values in an array */
double avg(double *a, int n) {
    int i;
    double total = 0.0;
    for (i = 0; i < n; i++) {
        total += a[i];
    }
    return total / n;
}

 封裝函數

/* Call double avg(double *, int) */
static PyObject *py_avg(PyObject *self, PyObject *args) {
  PyObject *bufobj;
  Py_buffer view;
  double result;
  /* Get the passed Python object */
  // 在一個C對象指針中儲存一個Python對象(沒有任何轉換)。
  // 所以,C程序接收傳遞的實際對象。對象的引用計數沒有增長。
  // 存儲的指針不是空的
  if (!PyArg_ParseTuple(args, "O", &bufobj)) {
    return NULL;
  }

  /* Attempt to extract buffer information from it */

  if (PyObject_GetBuffer(bufobj, &view,
      PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
    return NULL;
  }

  if (view.ndim != 1) {
    PyErr_SetString(PyExc_TypeError, "Expected a 1-dimensional array");
    PyBuffer_Release(&view);
    return NULL;
  }

  /* Check the type of items in the array */
  if (strcmp(view.format,"d") != 0) {
    PyErr_SetString(PyExc_TypeError, "Expected an array of doubles");
    PyBuffer_Release(&view);
    return NULL;
  }

  /* Pass the raw buffer and size to the C function */
  result = avg(view.buf, view.shape[0]);

  /* Indicate we're done working with the buffer */
  PyBuffer_Release(&view);
  return Py_BuildValue("d", result);
}

代碼的關鍵點在於 PyBuffer_GetBuffer() 函數。 給定一個任意的Python對象,它會試着去獲取底層內存信息,它簡單的拋出一個異常並返回-1. 傳給 PyBuffer_GetBuffer() 的特殊標誌給出了所需的內存緩衝類型。 例如,PyBUF_ANY_CONTIGUOUS 表示是一個連續的內存區域。github

if (PyObject_GetBuffer(bufobj, &view,
      PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
    return NULL;
  }

對於數組、字節字符串和其餘相似對象而言,一個 Py_buffer 結構體包含了全部底層內存的信息。 它包含一個指向內存地址、大小、元素大小、格式和其餘細節的指針。下面是這個結構體的定義:數組

typedef struct bufferinfo { void *buf; /* Pointer to buffer memory */ PyObject *obj; /* Python object that is the owner */ Py_ssize_t len; /* Total size in bytes */ Py_ssize_t itemsize; /* Size in bytes of a single item */ int readonly; /* Read-only access flag */ int ndim; /* Number of dimensions */ char *format; /* struct code of a single item */ Py_ssize_t *shape; /* Array containing dimensions */ Py_ssize_t *strides; /* Array containing strides */ Py_ssize_t *suboffsets; /* Array containing suboffsets */ } Py_buffer;

本節中,咱們只關注接受一個雙精度浮點數數組做爲參數。 要檢查元素是不是一個雙精度浮點數,只需驗證 format 屬性是否是字符串」d」. 這個也是 struct 模塊用來編碼二進制數據的。 一般來說,format 能夠是任何兼容 struct 模塊的格式化字符串, 而且若是數組包含了C結構的話它能夠包含多個值。緩存

/* Check the type of items in the array */
  if (strcmp(view.format,"d") != 0) {
    PyErr_SetString(PyExc_TypeError, "Expected an array of doubles");
    PyBuffer_Release(&view);
    return NULL;
  }

一旦咱們已經肯定了底層的緩存區信息,那隻須要簡單的將它傳給C函數,而後會被當作是一個普通的C數組了。 實際上,咱們沒必要擔憂是怎樣的數組類型或者它是被什麼庫建立出來的。 這也是爲何這個函數能兼容 array 模塊也能兼容 numpy 模塊中的數組了。ide

在返回最終結果以前,底層的緩衝區視圖必須使用 PyBuffer_Release() 釋放掉。 之因此要這一步是爲了能正確的管理對象的引用計數。函數

庫信息修改

/* Module method table */
static PyMethodDef SampleMethods[] = {
  {"gcd",  py_gcd, METH_VARARGS, "Greatest common divisor"},
  {"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
  {"divide", py_divide, METH_VARARGS, "Integer division"},
  {"avg", py_avg, METH_VARARGS, "Average values in an array"},
  { NULL, NULL, 0, NULL}
};

測試

python setup.py install測試

2、全程序展現

pysample.c全文以下,其餘部分並未修改,參見上節便可ui

#include "Python.h"
#include "sample.h"

/* int gcd(int, int) */
static PyObject *py_gcd(PyObject *self, PyObject *args) {
  int x, y, result;

  if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
    return NULL;
  }
  result = gcd(x,y);
  return Py_BuildValue("i", result);
}

/* int in_mandel(double, double, int) */
static PyObject *py_in_mandel(PyObject *self, PyObject *args) {
  double x0, y0;
  int n;
  int result;

  if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) {
    return NULL;
  }
  result = in_mandel(x0,y0,n);
  return Py_BuildValue("i", result);
}

/* int divide(int, int, int *) */
static PyObject *py_divide(PyObject *self, PyObject *args) {
  int a, b, quotient, remainder;
  if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
    return NULL;
  }
  quotient = divide(a,b, &remainder);
  return Py_BuildValue("(ii)", quotient, remainder);
}

/* Call double avg(double *, int) */
static PyObject *py_avg(PyObject *self, PyObject *args) {
  PyObject *bufobj;
  Py_buffer view;
  double result;
  /* Get the passed Python object */
  // 在一個C對象指針中儲存一個Python對象(沒有任何轉換)。
  // 所以,C程序接收傳遞的實際對象。對象的引用計數沒有增長。
  // 存儲的指針不是空的
  if (!PyArg_ParseTuple(args, "O", &bufobj)) {
    return NULL;
  }

  /* Attempt to extract buffer information from it */

  if (PyObject_GetBuffer(bufobj, &view,
      PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1) {
    return NULL;
  }

  if (view.ndim != 1) {
    PyErr_SetString(PyExc_TypeError, "Expected a 1-dimensional array");
    PyBuffer_Release(&view);
    return NULL;
  }

  /* Check the type of items in the array */
  if (strcmp(view.format,"d") != 0) {
    PyErr_SetString(PyExc_TypeError, "Expected an array of doubles");
    PyBuffer_Release(&view);
    return NULL;
  }

  /* Pass the raw buffer and size to the C function */
  result = avg(view.buf, view.shape[0]);

  /* Indicate we're done working with the buffer */
  PyBuffer_Release(&view);
  return Py_BuildValue("d", result);
}



/* Module method table */
static PyMethodDef SampleMethods[] = {
  {"gcd",  py_gcd, METH_VARARGS, "Greatest common divisor"},
  {"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
  {"divide", py_divide, METH_VARARGS, "Integer division"},
  {"avg", py_avg, METH_VARARGS, "Average values in an array"},
  { NULL, NULL, 0, NULL}
};

/* Module structure */
static struct PyModuleDef samplemodule = {
  PyModuleDef_HEAD_INIT,

  "sample",           /* name of module */
  "A sample module",  /* Doc string (may be NULL) */
  -1,                 /* Size of per-interpreter state or -1 */
  SampleMethods       /* Method table */
};

/* Module initialization function */
PyMODINIT_FUNC
PyInit_sample(void) {
  return PyModule_Create(&samplemodule);
}
相關文章
相關標籤/搜索