『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);
}
『Python CoolBook』C扩展库_其三_简单数组操作的更多相关文章
- 『Python CoolBook』C扩展库_其一_用法讲解
不依靠其他工具,直接使用Python的扩展API来编写一些简单的C扩展模块. 本篇参考PythonCookbook第15节和Python核心编程完成,值得注意的是,Python2.X和Python3. ...
- 『Python CoolBook』C扩展库_其五_C语言层面Python库之间调用API
点击进入项目 一.C层面模块添加API 我们仍然操作如下结构体, #include <math.h> typedef struct Point { double x,y; } Point; ...
- 『Python CoolBook』C扩展库_其六_线程
GIL操作 想让C扩展代码和Python解释器中的其他进程一起正确的执行, 那么你就需要去释放并重新获取全局解释器锁(GIL). 在Python接口封装中去释放并重新获取全局解释器锁(GIL),此时本 ...
- 『Python CoolBook』C扩展库_其二_demo演示
点击进入项目 C函数源文件 /* sample.c */ #include "sample.h" /* Compute the greatest common divisor */ ...
- 『Python CoolBook』C扩展库_其四_结构体操作与Capsule
点击进入项目 一.Python生成C语言结构体 C语言中的结构体传给Python时会被封装为胶囊(Capsule), 我们想要一个如下结构体进行运算,则需要Python传入x.y两个浮点数, type ...
- 『Python CoolBook』C扩展库_其六_从C语言中调用Python代码
点击进入项目 一.C语言运行pyfun的PyObject对象 思路是在C语言中提供实参,传给python函数: 获取py函数对象(PyObject),函数参数(C类型) 获取GIL(PyGILStat ...
- 『Python CoolBook』使用ctypes访问C代码_下_demo进阶
点击进入项目 这一次我们尝试一下略微复杂的c程序. 一.C程序 头文件: #ifndef __SAMPLE_H__ #define __SAMPLE_H__ #include <math.h&g ...
- 『Python CoolBook』使用ctypes访问C代码_上_用法讲解
一.动态库文件生成 源文件hello.c #include "hello.h" #include <stdio.h> void hello(const char *na ...
- 『Python CoolBook』Cython
github地址 使用Cython导入库的话,需要一下几个文件: .c:C函数源码 .h:C函数头 .pxd:Cython函数头 .pyx:包装函数 setup.py:python 本节示例.c和.h ...
随机推荐
- Windows 10 更改系统文字大小
一. Win + R 进入Regedit: 二. 定位到下图的位置: 三. 选中一个项目,右键,选中修改二进制,打开后如下图: 四. 1.这里,0000一行中的第一位,对应了图形界面设置中的字体大小. ...
- sql 范式:1NF、2NF、3NF、BCNF(函数依赖)
第一范式(1NF) 每个属性都是不可分的基本数据项.(必须有主键,列不可分) eg:非第一范式的表:(列可再分) 学院名称 高级职称人数 教授 副教授 信电学院 3 34 管理学院 5 23 外语学院 ...
- python获取一年所有的日期
python获取一年所有的日期 自动识别闰年. import arrow def isLeapYear(years): ''' 通过判断闰年,获取年份years下一年的总天数 :param years ...
- CSS 字体效果
text-shadow还没有出现时,大家在网页设计中阴影一般都是用photoshop做成图片,现在有了css3可以直接使用text-shadow属性来指定阴影.这个属性可以有两个作用,产生阴影和模糊主 ...
- Lambda表达式详解(例子详解)(转自:http://blog.csdn.net/damon316/article/details/51734661)
Lambda表达式详解(例子详解) lambda简介 lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他,“转到”或者 ...
- sublime-text3打造markdown编辑器
编辑插件 sublime自带的markdown语法高亮并不是很友好,推荐安装Markdown Editing,github主页然后在视图->语法里选择MarkdownEditing启用,支持三种 ...
- Vue系列之 => 评论功能(小知识点串联)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- [openjudge-动态规划]Maximum sum
题目描述 题目原文 描述 Given a set of n integers: A={a1, a2,-, an}, we define a function d(A) as below: d(A)=m ...
- 5、Kafka生产过程分析
1.写入方式 producer采用推(push)模式将消息发布到broker, 每条消息都被追加(append)到分区(patition)中,属于顺序写磁盘(顺序写磁盘效率比随机写内存要高,保障kaf ...
- kettle 连接 SQL Server 异常
场景重现 新安装的 kettle(pdi-ce-7.0.0.0-25) 连接 SQL Server 2012 时报错如下: 解决办法 到 https://sourceforge.net/project ...