数组接口协议#
备注
此页面描述了特定于 NumPy 的 API,用于从其他 C 扩展访问 NumPy 数组的内容. PEP 3118 – The Revised Buffer Protocol 引入了类似的,标准化的 API,供任何扩展模块使用. Cython’s buffer array support 使用了 PEP 3118 API;请参阅 Cython NumPy tutorial .
- 版本:
3
数组接口(有时称为数组协议)创建于 2005 年,作为类数组 Python 对象在可能的情况下智能地重用彼此的数据缓冲区的一种手段. 同质 N 维数组接口是对象共享 N 维数组内存和信息的默认机制. 该接口由 Python 端和 C 端组成,使用两个属性. 希望在应用程序代码中被视为 N 维数组的对象应至少支持其中一个属性. 希望在应用程序代码中支持 N 维数组的对象应查找至少其中一个属性并适当使用所提供的信息.
此接口描述了同质数组,因为数组的每个项目都具有相同的“类型”. 此类型可以非常简单,也可以是相当任意和复杂的 C 风格的结构.
有两种使用该接口的方式:Python 端和 C 端.两者都是单独的属性.
Python 端#
此接口方法包括对象具有 __array_interface__ 属性.
- object.__array_interface__#
一个项目字典(3 个必需项和 5 个可选项). 如果未提供字典中的可选键,则它们具有隐含的默认值.
键是:
- shape(必需)
元组,其元素是每个维度中的数组大小. 每个条目都是一个整数(一个 Python
int). 请注意,这些整数可能大于平台int或long可以容纳的整数(Pythonint是 Clong). 使用此属性的代码可以适当地处理此问题;或者在可能发生溢出时引发错误,或者使用long long作为形状的 C 类型.- typestr(必需)
提供同质数组基本类型的字符串.基本字符串格式包含 3 个部分:一个字符描述数据的字节顺序(
<:小端序,>:大端序,|:不相关),一个字符代码给出数组的基本类型,以及一个整数提供该类型使用的字节数.基本类型字符代码为:
- descr (可选)
一个元组列表,提供对同构数组中每个条目的内存布局的更详细的描述.列表中的每个元组都有两个或三个元素.通常,当 typestr 为
V[0-9]+时,将使用此属性,但这不是必需的.唯一的要求是 typestr 键中表示的字节数与此处表示的总字节数相同.这个想法是为了支持构成数组元素的类 C 结构的描述.列表中的每个元组的元素是一个字符串,提供与数据类型此部分相关的名称.这也可能是一个元组
('full name', 'basic_name'),其中 basic name 将是一个有效的 Python 变量名,表示该字段的全名.可以是像 typestr 中那样的基本类型描述字符串,也可以是另一个列表(对于嵌套的结构化类型)
一个可选的形状元组,提供该结构的这一部分应该重复多少次.如果未给出,则假定不重复.可以使用此通用接口描述非常复杂的结构.但是请注意,数组的每个元素仍然是相同的数据类型.下面给出了一些使用此接口的示例.
默认值:
[('', typestr)]- data (可选)
一个 2 元组,其第一个参数是一个 Python integer ,指向存储数组内容的data-area.
备注
当通过
PyLong_From*或高级绑定(如 Cython 或 pybind11)从 C/C++ 转换时,请确保使用足够大位数的整数.此指针必须指向数据的第一个元素(换句话说,在这种情况下总是忽略任何偏移).元组中的第二个条目是一个只读标志(true 表示数据区域是只读的).
此属性也可以是一个暴露 buffer interface 的对象,该对象将用于共享数据.如果此键不存在(或返回 None),则将通过对象本身的buffer接口完成内存共享.在这种情况下,可以使用 offset 键来指示缓冲区的开始.如果要保护内存区域,则必须由新对象存储对暴露数组接口的对象的引用.
默认值:
None- strides (可选)
可以是
None以指示 C 风格的连续数组,也可以是 strides 元组,该元组提供跳转到相应维度中下一个数组元素所需的字节数.每个条目必须是一个整数(一个 Pythonint).与 shape 一样,这些值可能大于 Cint或long可以表示的值;调用代码应适当处理此问题,可以通过引发错误或在 C 中使用long long.默认值为None,表示 C 风格的连续内存缓冲区.在此模型中,数组的最后一个维度变化最快.例如,对于数组条目为 8 字节长且形状为(10, 20, 30)的对象,默认的 strides 元组将是(4800, 240, 8).默认值:
None(C 风格连续)- mask (可选)
None或一个暴露数组接口的对象.mask 数组的所有元素都应仅解释为 true 或 not true,指示此数组的哪些元素有效.此对象的形状应为 “broadcastable” 到原始数组的形状.默认值:
None(所有数组值均有效)- offset(可选)
一个指向数组数据区域的整数偏移量.只有当 data 为
None或返回一个memoryview对象时才能使用.默认值:
0.- version(必需)
一个整数,表示接口的版本(即,此版本为 3).注意不要使用它来使公开未来版本的接口的对象失效.
C 结构体访问#
这种数组接口的方法允许仅使用一个属性查找和一个定义良好的 C 结构体来更快地访问数组.
- object.__array_struct__#
一个
PyCapsule,其pointer成员包含一个指向已填充PyArrayInterface结构的指针.结构的内存是动态创建的,并且PyCapsule也使用适当的析构函数创建,因此此属性的检索器只需在完成时将Py_DECREF应用于此属性返回的对象.此外,要么需要复制出数据,要么必须保持对公开此属性的对象的引用,以确保数据不会被释放.如果其他对象引用它们,则公开__array_struct__接口的对象也不得重新分配其内存.
PyArrayInterface 结构在 numpy/ndarrayobject.h 中定义为:
typedef struct {
int two; /* contains the integer 2 -- simple sanity check */
int nd; /* number of dimensions */
char typekind; /* kind in array --- character code of typestr */
int itemsize; /* size of each element */
int flags; /* flags indicating how the data should be interpreted */
/* must set ARR_HAS_DESCR bit to validate descr */
Py_ssize_t *shape; /* A length-nd array of shape information */
Py_ssize_t *strides; /* A length-nd array of stride information */
void *data; /* A pointer to the first element of the array */
PyObject *descr; /* NULL or data-description (same as descr key
of __array_interface__) -- must set ARR_HAS_DESCR
flag or this will be ignored. */
} PyArrayInterface;
flags 成员可能包含 5 位,显示应如何解释数据,以及 1 位,显示应如何解释接口.数据位为 NPY_ARRAY_C_CONTIGUOUS (0x1), NPY_ARRAY_F_CONTIGUOUS (0x2), NPY_ARRAY_ALIGNED (0x100), NPY_ARRAY_NOTSWAPPED (0x200), 和 NPY_ARRAY_WRITEABLE (0x400). final flag NPY_ARR_HAS_DESCR (0x800) 指示此结构是否具有 arrdescr 字段.除非存在此标志,否则不应访问该字段.
-
NPY_ARR_HAS_DESCR#
自 2006 年 6 月 16 日起新增:
过去,大多数实现使用 PyCObject (现在是 PyCapsule )本身的 desc 成员(不要将其与上面 PyArrayInterface 结构的“descr”成员混淆—它们是两个独立的事物)来保存指向公开接口的对象的指针.现在,这是接口的显式部分.在返回 PyCapsule_SetContext 之前,请务必获取对该对象的引用并调用 PyCapsule ,并配置析构函数以减少此引用.
备注
__array_struct__ 被认为是遗留的,不应用于新代码.请改用 buffer protocol 或 DLPack 协议 numpy.from_dlpack .
类型描述示例#
为了清楚起见,提供一些类型描述和相应的 __array_interface__ ‘descr’ 条目的示例很有用.感谢 Scott Gilbert 提供的这些示例:
在每种情况下,’descr’ 键都是可选的,但当然提供了更多信息,这些信息对于各种应用程序可能很重要:
* Float data
typestr == '>f4'
descr == [('','>f4')]
* Complex double
typestr == '>c8'
descr == [('real','>f4'), ('imag','>f4')]
* RGB Pixel data
typestr == '|V3'
descr == [('r','|u1'), ('g','|u1'), ('b','|u1')]
* Mixed endian (weird but could happen).
typestr == '|V8' (or '>u8')
descr == [('big','>i4'), ('little','<i4')]
* Nested structure
struct {
int ival;
struct {
unsigned short sval;
unsigned char bval;
unsigned char cval;
} sub;
}
typestr == '|V8' (or '<u8' if you want)
descr == [('ival','<i4'), ('sub', [('sval','<u2'), ('bval','|u1'), ('cval','|u1') ]) ]
* Nested array
struct {
int ival;
double data[16*4];
}
typestr == '|V516'
descr == [('ival','>i4'), ('data','>f8',(16,4))]
* Padded structure
struct {
int ival;
double dval;
}
typestr == '|V16'
descr == [('ival','>i4'),('','|V4'),('dval','>f8')]
应该清楚的是,可以使用此接口描述任何结构化类型.
与数组接口(版本 2)的差异#
版本 2 的接口非常相似.差异主要在于美学.特别是:
PyArrayInterface 结构在末尾没有 descr 成员 (因此也没有标志 ARR_HAS_DESCR)
从
__array_struct__返回的PyCapsule的context成员(正式地是PyCObject的desc成员)未指定.通常,它是公开数组的对象(以便可以保留对其的引用并在 C 对象被销毁时销毁).现在有一个明确的要求,即必须以某种方式使用此字段来保存对所有者对象的引用.备注
截至 2020 年 8 月,此处的内容为:
现在它必须是一个元组,其第一个元素是带有 “PyArrayInterface Version #” 的字符串,第二个元素是公开数组的对象.
此设计在提出后几乎立即被撤回,参见 <https://mail.python.org/pipermail/numpy-discussion/2006-June/020995.html>. 尽管有 14 年的相反文档,但在任何时候假设
__array_interface__胶囊包含此元组内容都是无效的.从
__array_interface__['data']返回的元组曾经是一个十六进制字符串(现在它是一个整数或长整数).以前没有
__array_interface__属性,而是__array_interface__字典中的所有键(版本除外)都是它们自己的属性:因此,要获取 Python 端的信息,您必须单独访问以下属性:__array_data____array_shape____array_strides____array_typestr____array_descr____array_offset____array_mask__