缓冲区和 Memoryview 对象 — Python 文档

来自菜鸟教程
Python/docs/2.7/c-api/buffer
跳转至:导航、​搜索

缓冲区和 Memoryview 对象

用 C 实现的 Python 对象可以导出一组称为“缓冲区接口”的函数。 对象可以使用这些函数以原始的、面向字节的格式公开其数据。 对象的客户端可以使用缓冲区接口直接访问对象数据,而无需先复制它。

支持缓冲区接口的对象的两个示例是字符串和数组。 字符串对象以缓冲区接口的面向字节的形式公开字符内容。 数组只能通过旧式缓冲区接口公开其内容。 此限制不适用于 Python 3,其中 memoryview 对象也可以从数组构造。 数组元素可以是多字节值。

缓冲区接口的一个示例用户是文件对象的 write() 方法。 任何可以通过缓冲区接口导出一系列字节的对象都可以写入文件。 PyArg_ParseTuple() 有许多格式代码,它们针对对象的缓冲区接口进行操作,从目标对象返回数据。

从版本 1.6 开始,Python 一直提供 Python 级别的缓冲区对象和 C 级别的缓冲区 API,以便任何内置或使用定义的类型都可以公开其特性。 然而,由于各种缺点,两者都已被弃用,并已在 Python 3 中正式删除,以支持新的 C 级缓冲区 API 和名为 memoryview 的新 Python 级对象。

新的缓冲区 API 已向后移植到 Python 2.6,memoryview 对象已向后移植到 Python 2.7。 强烈建议使用它们而不是旧的 API,除非出于兼容性原因禁止您这样做。

新式 Py_buffer 结构

type Py_buffer
void *buf

指向对象内存开始的指针。

int readonly

缓冲区是否只读的指示符。

int ndim

内存表示为多维数组的维数。 如果是 0stridessuboffsets 必须是 NULL

Py_ssize_t *shape

Py_ssize_t 的长度为 ndim 的数组给出了作为多维数组的内存形状。 请注意,((*shape)[0] * ... * (*shape)[ndims-1])*itemsize 应等于 len

Py_ssize_t *strides

Py_ssize_t 的数组 ndim 的长度给出了要跳过以到达每个维度中的新元素的字节数。

Py_ssize_t *suboffsets

Py_ssize_t 的数组 ndim 的长度。 如果这些子偏移量大于或等于 0,则沿指示维度存储的值是一个指针,子偏移值指示在取消引用后要添加到指针的字节数。 一个负的 suboffset 值表示不应发生取消引用(跨连续内存块)。

如果所有的子偏移都是负的(即 不需要取消引用),则该字段必须为 NULL(默认值)。

这是一个函数,当存在非 NULL 步幅和子偏移时,该函数返回指向 N 维索引指向的 ND 数组中元素的指针:

void *get_item_pointer(int ndim, void *buf, Py_ssize_t *strides,
    Py_ssize_t *suboffsets, Py_ssize_t *indices) {
    char *pointer = (char*)buf;
    int i;
    for (i = 0; i < ndim; i++) {
        pointer += strides[i] * indices[i];
        if (suboffsets[i] >=0 ) {
            pointer = *((char**)pointer) + suboffsets[i];
        }
    }
    return (void*)pointer;
 }
Py_ssize_t itemsize

这是共享内存中每个元素的项目大小(以字节为单位)的存储。 它在技术上是不必要的,因为它可以使用 PyBuffer_SizeFromFormat() 获得,但是导出器可能知道此信息而无需解析格式字符串,并且有必要知道 itemsize 以正确解释跨步。 因此,存放起来更加方便快捷。

void *internal

这是供导出对象在内部使用。 例如,这可能会被导出器重新转换为整数,并用于存储有关在释放缓冲区时是否必须释放形状、步幅和子偏移量数组的标志。 消费者永远不应该改变这个值。


内存视图对象

2.7 版中的新功能。


memoryview 对象将新的 C 级缓冲区接口公开为 Python 对象,然后可以像任何其他对象一样传递它。

PyObject *PyMemoryView_FromObject(PyObject *obj)
从定义新缓冲区接口的对象创建 memoryview 对象。
PyObject *PyMemoryView_FromBuffer(Py_buffer *view)
创建一个包含给定缓冲区信息结构 view 的 memoryview 对象。 memoryview 对象然后拥有缓冲区,这意味着您不应该尝试自己释放它:它将在 memoryview 对象的释放时释放。
PyObject *PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order)
从定义缓冲区接口的对象创建一个内存视图对象到连续的内存块(以“C”或“F”ortran order)。 如果内存是连续的,则 memoryview 对象指向原始内存。 否则进行复制并且 memoryview 指向一个新的字节对象。
int PyMemoryView_Check(PyObject *obj)
如果对象 obj 是 memoryview 对象,则返回 true。 当前不允许创建 memoryview 的子类。
Py_buffer *PyMemoryView_GET_BUFFER(PyObject *obj)
返回一个指向由给定对象包装的缓冲区信息结构的指针。 对象必须是一个memoryview实例; 这个宏不检查它的类型,你必须自己做,否则你会有崩溃的风险。


旧式缓冲区对象

有关旧缓冲区接口的更多信息,请参见 缓冲区对象结构 部分,在 PyBufferProcs 的描述下。

“缓冲区对象”在 bufferobject.h 标头中定义(包含在 Python.h 中)。 这些对象在 Python 编程级别看起来与字符串对象非常相似:它们支持切片、索引、连接和一些其他标准字符串操作。 但是,它们的数据可以来自以下两个来源之一:来自内存块,或来自导出缓冲区接口的另一个对象。

缓冲区对象作为一种将来自另一个对象的缓冲区接口的数据公开给 Python 程序员的方法非常有用。 它们也可以用作零拷贝切片机制。 使用它们引用内存块的能力,可以很容易地向 Python 程序员公开任何数据。 内存可以是 C 扩展中的一个大型常量数组,也可以是在传递到操作系统库之前用于操作的原始内存块,或者它可以用于传递其原生内存格式的结构化数据.

type PyBufferObject
PyObject 的这个子类型表示一个缓冲区对象。
PyTypeObject PyBuffer_Type
PyTypeObject的实例,代表Python缓冲区类型; 它与 Python 层中的 buffertypes.BufferType 是相同的对象。 .
int Py_END_OF_BUFFER
该常量可以作为 size 参数传递给 PyBuffer_FromObject()PyBuffer_FromReadWriteObject()。 它表示新的 PyBufferObject 应该从指定的 offset 到其导出缓冲区的末尾引用 base 对象。 使用它可以使调用者避免查询 base 对象的长度。
int PyBuffer_Check(PyObject *p)
如果参数的类型为 PyBuffer_Type,则返回 true。
PyObject *PyBuffer_FromObject(PyObject *base, Py_ssize_t offset, Py_ssize_t size)

返回一个新的只读缓冲区对象。 如果 base 不支持只读缓冲区协议或不提供一个缓冲区段,则会引发 TypeError;如果 偏移,则会引发 ValueError 小于零。 缓冲区将保存对 base 对象的引用,缓冲区的内容将引用 base 对象的缓冲区接口,从位置 offset 开始并扩展为 [ X202X]size 字节。 如果 sizePy_END_OF_BUFFER,则新缓冲区的内容将扩展到 base 对象导出的缓冲区数据的长度。

在 2.5 版更改:此函数使用 int 类型用于 偏移大小。 这可能需要更改您的代码才能正确支持 64 位系统。

PyObject *PyBuffer_FromReadWriteObject(PyObject *base, Py_ssize_t offset, Py_ssize_t size)

返回一个新的可写缓冲区对象。 参数和异常类似于 PyBuffer_FromObject()。 如果 base 对象不导出可写缓冲协议,则引发 TypeError

在 2.5 版更改:此函数使用 int 类型用于 偏移大小。 这可能需要更改您的代码才能正确支持 64 位系统。

PyObject *PyBuffer_FromMemory(void *ptr, Py_ssize_t size)

返回一个新的只读缓冲区对象,该对象从内存中的指定位置读取,具有指定的大小。 调用者负责确保作为 ptr 传入的内存缓冲区在返回的缓冲区对象存在时不会被释放。 如果 size 小于零,则引发 ValueError。 注意 Py_END_OF_BUFFER 可能 not 传递给 size 参数; 在这种情况下,ValueError 将升高。

2.5 版更改: 此函数使用 int 类型作为 大小 。 这可能需要更改您的代码才能正确支持 64 位系统。

PyObject *PyBuffer_FromReadWriteMemory(void *ptr, Py_ssize_t size)

类似于 PyBuffer_FromMemory(),但返回的缓冲区是可写的。

2.5 版更改: 此函数使用 int 类型作为 大小 。 这可能需要更改您的代码才能正确支持 64 位系统。

PyObject *PyBuffer_New(Py_ssize_t size)

返回一个新的可写缓冲区对象,该对象维护自己的 size 字节的内存缓冲区。 如果 size 不为零或正数,则返回 ValueError。 请注意,内存缓冲区(由 PyObject_AsWriteBuffer() 返回)没有特别对齐。

2.5 版更改: 此函数使用 int 类型作为 大小 。 这可能需要更改您的代码才能正确支持 64 位系统。