• 『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);
    }
    
  • 相关阅读:
    OpenSSL生成rsa密钥对
    RabbitMQ工作模式
    加密解密
    MongDB优化
    java线程进程
    MongoDB数据类型
    获取指针指向的内存大小方法
    [教程] 让Mac OS 10.x.x安装在Vmware虚拟机上!
    安装好的苹果系统部分截图
    VC中MFC程序手动控制最近文件列表
  • 原文地址:https://www.cnblogs.com/hellcat/p/9088524.html
Copyright © 2020-2023  润新知