On this page
缓冲区和 Memoryview 对象
用 C 实现的 Python 对象可以导出一组称为“缓冲区接口”的函数。对象可以使用这些函数以原始的,面向字节的格式公开其数据。对象的 Client 端可以使用缓冲区接口直接访问对象数据,而无需先复制它。
支持缓冲区接口的对象的两个示例是字符串和数组。字符串对象以缓冲区接口面向字节的形式公开字符内容。数组只能pass旧式缓冲区接口公开其内容。此限制不适用于 Python 3,其中memoryview对象也可以从数组构造。数组元素可以是多字节值。
缓冲区接口的一个示例用户是文件对象的write()
方法。可以pass缓冲区接口导出一系列字节的任何对象都可以写入文件。 PyArg_ParseTuple()有许多格式代码可对对象的缓冲区接口进行操作,从目标对象返回数据。
从 1.6 版开始,Python 一直提供 Python 级别的缓冲区对象和 C 级别的缓冲区 API,以便任何内置或使用定义的类型都可以公开其特性。但是,由于各种缺点,两者均已弃用,并已在 Python 3 中正式删除,以支持新的 C 级缓冲区 API 和名为memoryview的新 Python 级对象。
新的缓冲区 API 已回传到 Python 2.6,而memoryview对象已回传到 Python 2.7. 强烈建议使用它们而不是旧的 API,除非出于兼容性原因而被禁止这样做。
new-style Py_buffer 结构
Py_buffer
-
- 无效*
buf
- 指向对象内存开始的指针。
- 无效*
-
Py_ssize_t
len
- 内存的总长度(以字节为单位)。
int
readonly
- 缓冲区是否为只读的指示器。
const char *
format
- struct模块样式语法中的* NULL 终止字符串,提供了可pass缓冲区使用的元素的内容。如果这是 NULL *,则假定
"B"
(无符号字节)。
- struct模块样式语法中的* NULL 终止字符串,提供了可pass缓冲区使用的元素的内容。如果这是 NULL *,则假定
int
ndim
- 存储器表示的维数为多维数组。如果为
0
,则strides
和suboffsets
必须为* NULL *。
- 存储器表示的维数为多维数组。如果为
Py_ssize_t *
shape
Py_ssize_t
的数组,长度为ndim
的数组,将内存的形状表示为多维数组。注意((*shape)[0] * ... * (*shape)[ndims-1])*itemsize
应该等于len
。
Py_ssize_t *
strides
Py_ssize_t
s 数组的长度为ndim
的数组,给出在每个维中跳转到新元素所要跳过的字节数。
Py_ssize_t *
suboffsets
Py_ssize_t
的数组,长度为ndim
。如果这些子偏移量数字大于或等于 0,则沿指示的维存储的值是一个指针,并且子偏移量值指示在取消引用后要添加到指针的字节数。负的子偏移值表示不应取消引用(跨连续内存块)。
如果所有子偏移量均为负(即不需要取消引用),则此字段必须为 NULL(默认值)。
这是一个函数,当同时存在非 NULL 步幅和子偏移量时,该函数返回指向 N 维索引所指向的 N 维数组中元素的指针:
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()来获取它,但是 Export 商可以在不解析格式字符串的情况下知道此信息,并且有必要知道项的大小以正确理解步幅。因此,存储起来更加方便快捷。
无效*
internal
- 供导出对象在内部使用。例如,导出器可能会将其重新 Broadcast 为整数,并用于存储有关释放缓冲区时是否必须释放形状,步幅和子偏移数组的标志。Consumer 切勿更改此值。
缓冲区相关Function
int
PyObject_CheckBuffer
(PyObject ** obj *)- 如果* obj *支持缓冲区接口,则返回
1
,否则返回0
。
- 如果* obj *支持缓冲区接口,则返回
int
PyObject_GetBuffer
(PyObject *obj ,Py_buffer view *,int * flags *)- 将* obj 导出到Py_buffer, view 。这些参数绝不能为 NULL *。 * flags *参数是一个位字段,指示调用者准备处理哪种缓冲区,以及允许导出程序返回哪种缓冲区。缓冲区接口允许进行复杂的内存共享,但是某些调用者可能无法处理所有复杂性,但可能希望查看导出器是否允许它们对内存进行简单的查看。
一些 Export 商可能无法以所有可能的方式共享内存,并且可能需要引发错误以向某些 Consumer 发出 signal,表明某些事情是不可能的。这些错误应该是BufferError,除非确实有另一个错误导致了该问题。导出器可以使用标志信息来简化Py_buffer结构中非默认值的填充量,并且/或者如果对象不支持其内存的更简单视图,则会引发错误。
成功返回0
,错误返回-1
。
下表为* flags 参数提供了可能的值。
Flag | Description | |
---|---|---|
PyBUF_SIMPLE |
这是默认标志状态。返回的缓冲区可能有也可能没有可写内存。数据的格式将假定为无符号字节。这是一个“独立”标志常量。它永远不需要对其他人“ | ”。如果导出器无法提供这种连续的字节缓冲区,则会引发错误。 |
PyBUF_WRITABLE |
返回的缓冲区必须可写。如果不可写,则引发错误。 | |
PyBUF_STRIDES |
这意味着PyBUF_ND 。返回的缓冲区必须提供步幅信息(即,步幅不能为 NULL)。当使用者可以处理跨步,不连续的数组时,将使用此方法。处理步幅自动假定您可以处理形状。如果无法跨步表示数据(即没有子偏移),则导出器可能会引发错误。 |
|
PyBUF_ND |
返回的缓冲区必须提供形状信息。内存将假定为 C 样式连续的(最后一个尺寸变化最快)。如果导出器无法提供这种连续的缓冲区,则可能会引发错误。如果未给出,则形状将为* NULL *。 | |
PyBUF_C_CONTIGUOUS PyBUF_F_CONTIGUOUS PyBUF_ANY_CONTIGUOUS |
这些标志指示连续性返回的缓冲区必须分别是 C 连续的(最后一个维度变化最快),Fortran 连续的(第一个维度变化最快)或任意一个。所有这些标志均表示PyBUF_STRIDES ,并确保跨步缓冲区信息结构将正确填写。 |
|
PyBUF_INDIRECT |
该标志指示返回的缓冲区必须具有子偏移信息(如果不需要子偏移,则可以为 NULL)。当使用者可以处理这些子偏移量隐含的间接数组引用时,可以使用此方法。这意味着PyBUF_STRIDES 。 |
|
PyBUF_FORMAT |
如果提供了此标志,则返回的缓冲区必须具有真实的格式信息。当 Consumer 要检查实际存储的是哪种“数据”时,将使用此方法。如果要求,Export 商应始终能够提供此信息。如果未显式请求格式,则格式必须以* NULL *(表示'B' 或无符号字节)形式返回。 |
|
PyBUF_STRIDED |
这等效于(PyBUF_STRIDES | PyBUF_WRITABLE) 。 |
|
PyBUF_STRIDED_RO |
这等效于(PyBUF_STRIDES) 。 |
|
PyBUF_RECORDS |
这等效于(PyBUF_STRIDES | PyBUF_FORMAT | PyBUF_WRITABLE) 。 |
|
PyBUF_RECORDS_RO |
这等效于(PyBUF_STRIDES | PyBUF_FORMAT) 。 |
|
PyBUF_FULL |
这等效于(PyBUF_INDIRECT | PyBUF_FORMAT | PyBUF_WRITABLE) 。 |
|
PyBUF_FULL_RO |
这等效于(PyBUF_INDIRECT | PyBUF_FORMAT) 。 |
|
PyBUF_CONTIG |
这等效于(PyBUF_ND | PyBUF_WRITABLE) 。 |
|
PyBUF_CONTIG_RO |
这等效于(PyBUF_ND) 。 |
MemoryView objects
2.7 版的新Function。
memoryview对象将新的 C 级缓冲区接口公开为 Python 对象,然后可以像其他任何对象一样传递该对象。
PyObject *
PyMemoryView_FromObject
(PyObject ** obj *)- 从定义新缓冲区接口的对象创建一个 memoryview 对象。
PyObject *
PyMemoryView_FromBuffer
(Py_buffer *查看)- 创建一个包装给定 buffer-info 结构* view *的 memoryview 对象。然后,memoryview 对象拥有缓冲区,这意味着您不应该自己try释放它:它将在释放 memoryview 对象时释放。
PyObject *
PyMemoryView_GetContiguous
(PyObject ** obj *,int * buffertype *,char * order *)- 从定义缓冲区接口的对象向连续的内存块(在'C'或'F'ortran * order *)中创建一个 memoryview 对象。如果内存是连续的,则 memoryview 对象指向原始内存。否则,将进行复制,并且 memoryview 指向新的字节对象。
int
PyMemoryView_Check
(PyObject ** obj *)- 如果对象* obj *是 memoryview 对象,则返回 true。当前不允许创建memoryview的子类。
Py_buffer *
PyMemoryView_GET_BUFFER
(PyObject ** obj *)- 返回指向给定对象包装的 buffer-info 结构的指针。对象**必须是一个 memoryview 实例;此宏不会检查其类型,您必须自己执行此操作,否则可能会崩溃。
旧式缓冲区对象
有关旧缓冲区接口的更多信息,请参见缓冲区对象结构部分,位于PyBufferProcs的描述下。
在bufferobject.h
Headers(由Python.h
包含)中定义了一个“缓冲区对象”。这些对象在 Python 编程级别上看起来与字符串对象非常相似:它们支持切片,索引,串联和其他一些标准的字符串操作。但是,它们的数据可以来自以下两个来源之一:来自一个内存块,或来自另一个导出缓冲区接口的对象。
缓冲区对象非常有用,可以将数据从另一个对象的缓冲区接口公开给 Python 程序员。它们也可以用作零拷贝切片机制。利用它们引用内存块的能力,可以很容易地将任何数据公开给 Python 程序员。内存可以是 C 扩展中的大型恒定数组,也可以是原始内存块,以便在传递给 os 库之前进行操作,或者可以用于以本机内存形式传递结构化数据。
PyBufferObject
- PyObject的此子类型表示缓冲区对象。
PyTypeObject
PyBuffer_Type
- PyTypeObject的实例,表示 Python 缓冲区类型;它与 Python 层中的
buffer
和types.BufferType
是同Pair象。 。
- PyTypeObject的实例,表示 Python 缓冲区类型;它与 Python 层中的
int
Py_END_OF_BUFFER
- 该常量可以作为* size 参数传递给PyBuffer_FromObject()或PyBuffer_FromReadWriteObject()。它指示新的PyBufferObject应该从指定的 offset 到其导出缓冲区的末尾引用 base 对象。使用此Function,调用者可以避免查询 base *对象的长度。
int
PyBuffer_Check
(PyObject ** p *)- 如果参数的类型为PyBuffer_Type,则返回 true。
PyObject *
PyBuffer_FromObject
(PyObject ** base *,Py_ssize_t * offset *,Py_ssize_t * size *)- 返回值:新参考.
返回一个新的只读缓冲区对象。如果* base 不支持只读缓冲区协议或不完全提供一个缓冲区段,则产生TypeError;如果 offset 小于零,则产生ValueError。缓冲区将保存对 base 对象的引用,缓冲区的内容将指向 base 对象的缓冲区接口,从位置 offset 开始,并扩展为 size 字节。如果 size 为Py_END_OF_BUFFER
,则新缓冲区的内容将扩展为 base *对象导出的缓冲区数据的长度。
在版本 2.5 中进行了更改:此函数将int
类型用于* offset 和 size *。这可能需要更改您的代码以正确支持 64 位系统。
- PyObject *
PyBuffer_FromReadWriteObject
(PyObject ** base *,Py_ssize_t * offset *,Py_ssize_t * size *)- 返回值:新参考.
返回一个新的可写缓冲区对象。参数和异常与PyBuffer_FromObject()相似。如果* base *对象未导出可写缓冲区协议,则引发TypeError。
在版本 2.5 中进行了更改:此函数将int
类型用于* offset 和 size *。这可能需要更改您的代码以正确支持 64 位系统。
- PyObject *
PyBuffer_FromMemory
(void ** ptr *,Py_ssize_t * size *)- 返回值:新参考.
返回一个新的只读缓冲区对象,该对象从内存中的指定位置读取并具有指定的大小。调用者负责确保在返回的缓冲区对象存在时,不释放作为* ptr 传入的内存缓冲区。如果 size 小于零,则加ValueError。注意Py_END_OF_BUFFER
可能不传递给 size *参数。在这种情况下,将引发ValueError。
在版本 2.5 中进行了更改:此Function为* size *使用了int
类型。这可能需要更改您的代码以正确支持 64 位系统。
- PyObject *
PyBuffer_FromReadWriteMemory
(void ** ptr *,Py_ssize_t * size *)- 返回值:新参考.
与PyBuffer_FromMemory()相似,但是返回的缓冲区是可写的。
在版本 2.5 中进行了更改:此Function为* size *使用了int
类型。这可能需要更改您的代码以正确支持 64 位系统。
- PyObject *
PyBuffer_New
(Py_ssize_t * size *)- 返回值:新参考.
返回一个新的可写缓冲区对象,该对象保持其自己的* size 字节的内存缓冲区。如果 size *不是零或正数,则返回ValueError。请注意,内存缓冲区(由PyObject_AsWriteBuffer()返回)未明确对齐。
在版本 2.5 中进行了更改:此Function为* size *使用了int
类型。这可能需要更改您的代码以正确支持 64 位系统。