zoukankan      html  css  js  c++  java
  • 『Python CoolBook』C扩展库_其三_简单数组操作

    点击进入项目

    这里的数组要点在于:

    • 数组结构,array.array或者numpy.array
    • 本篇的数组仅限一维,不过基础的C数组也是一维

    一、分块讲解

    源函数

    /* 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 表示是一个连续的内存区域。

    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 模块中的数组了。

    在返回最终结果之前,底层的缓冲区视图必须使用 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

    二、全程序展示

    pysample.c全文如下,其他部分并未修改,参见上节即可

    #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);
    }
    
  • 相关阅读:
    Android游戏开发22:Android动画的实现J2me游戏类库用于Android开发
    android sqlite SQLiteDatabase 操作大全 不看后悔!必收藏!看后精通SQLITE (第三部分,完整代码)
    使用OGR创建dxf格式矢量数据
    mysql 数据库引擎 MyISAM InnoDB 大比拼 区别
    android sqlite SQLiteDatabase 操作大全 不看后悔!必收藏!看后精通SQLITE (第二部分)
    mysql 更改数据库引擎
    android sqlite SQLiteDatabase 操作大全 不看后悔!必收藏!看后精通SQLITE (第一部分)
    android 数字键盘使用
    MySQL Innodb数据库性能实践
    eclipse : Error while performing database login with the driver null
  • 原文地址:https://www.cnblogs.com/hellcat/p/9088524.html
Copyright © 2011-2022 走看看