阵列接口协议#
笔记
本页介绍了 NumPy 特定的 API,用于从其他 C 扩展访问 NumPy 数组的内容。PEP 3118 –
引入了与 Python 2.6 和 3.0 类似的标准化 API,供任何扩展模块使用。 Cython的缓冲区数组支持使用The Revised Buffer Protocol
PEP 3118 API;请参阅Cython NumPy 教程。 Cython 提供了一种使用早于 2.6 的 Python 版本编写支持缓冲区协议的代码的方法,因为它具有利用此处描述的数组接口的向后兼容实现。
- 版本:
3
数组接口(有时称为数组协议)创建于 2005 年,作为类似数组的 Python 对象在可能的情况下智能地重用彼此的数据缓冲区的一种手段。同构N维数组接口是对象共享N维数组内存和信息的默认机制。该接口由使用两个属性的 Python 端和 C 端组成。希望在应用程序代码中被视为 N 维数组的对象应该至少支持这些属性之一。希望在应用程序代码中支持 N 维数组的对象应该至少查找这些属性之一并适当地使用所提供的信息。
该接口描述了同构数组,即数组的每个项目都具有相同的“类型”。这种类型可以非常简单,也可以是相当任意且复杂的类似 C 的结构。
有两种使用该接口的方式:Python 端和 C 端。两者都是单独的属性。
Python 端#
这种接口方法由具有
__array_interface__
属性的对象组成。
- 目的。__数组接口__ #
项目字典(3 个必需项和 5 个可选项)。如果未提供,字典中的可选键将具有隐含的默认值。
关键是:
- 形状(必填)
元组,其元素是每个维度的数组大小。每个条目都是一个整数(Python
int
)。请注意,这些整数可能大于平台int
或long
可以容纳(Pythonint
是 Clong
)。由使用此属性的代码来适当地处理这个问题;要么在可能溢出时引发错误,要么用作 形状的 C 类型。long long
- 类型str(必填)
提供同构数组基本类型的字符串基本字符串格式由 3 部分组成:描述数据字节顺序的字符(
<
:小端、>
:大端、|
:不相关)、给出基本类型的字符代码数组的类型,以及提供该类型使用的字节数的整数。基本类型字符代码是:
t
位字段(以下整数给出位字段中的位数)。
b
布尔值(所有值都仅为
True
或 的 整数类型False
)i
整数
u
无符号整数
f
浮点
c
复杂浮点数
m
时间增量
M
约会时间
O
对象(即内存包含指向 的指针
PyObject
)S
字符串(固定长度的字符序列)
U
Unicode(固定长度序列
Py_UCS4
)V
其他(void * – 每个项目都是固定大小的内存块)
- 描述(可选)
元组列表,提供同构数组中每个项目的内存布局的更详细描述。列表中的每个元组都有两个或三个元素。通常,当typestr为时,将使用此属性
V[0-9]+
,但这不是必需的。唯一的要求是typestr键中表示的字节数与此处表示的字节总数相同。这个想法是支持构成数组元素的类 C 结构的描述。列表中每个元组的元素是提供与数据类型的此部分关联的名称的字符串。这也可以是一个元组,其中基本名称是表示字段全名的有效 Python 变量名称。
('full name', 'basic_name')
typestr中的基本类型描述字符串或另一个列表(对于嵌套结构类型)
可选的形状元组,提供结构的这一部分应重复的次数。如果未给出,则假定没有重复。使用这个通用接口可以描述非常复杂的结构。但请注意,数组的每个元素仍然具有相同的数据类型。下面给出了使用此接口的一些示例。
默认:
[('', typestr)]
- 数据(可选)
一个 2 元组,其第一个参数是一个Python 整数 ,指向存储数组内容的数据区域。
笔记
PyLong_From*
当通过Cython 或 pybind11 等高级绑定从 C/C++ 转换时,请确保使用足够大位数的整数。该指针必须指向数据的第一个元素(换句话说,在这种情况下始终忽略任何偏移量)。元组中的第二个条目是只读标志(true 表示数据区域是只读的)。
该属性也可以是公开用于共享数据的缓冲区接口的对象 。如果该键不存在(或返回 None),则内存共享将通过对象本身的缓冲区接口完成。在这种情况下,偏移键可以用来指示缓冲区的开始。如果要保证内存区域的安全,新对象必须存储对公开数组接口的对象的引用。
默认:
None
- 步幅(可选)
指示
None
C 风格的连续数组或步幅元组,它提供跳转到相应维度中的下一个数组元素所需的字节数。每个条目必须是整数(Pythonint
)。与形状一样,这些值可能大于 Cint
或所表示的值long
;调用代码应该适当地处理这个问题,要么引发错误,要么在 C 中使用。默认值意味着 C 风格的连续内存缓冲区。在此模型中,数组的最后一个维度变化最快。例如,数组条目长度为 8 个字节且形状为 的对象的默认步幅元组将为。long long
None
(10, 20, 30)
(4800, 240, 8)
默认值:
None
(C 风格连续)- 面罩(可选)
None
或暴露数组接口的对象。掩码数组的所有元素应仅解释为 true 或 not true,指示该数组的哪些元素有效。该对象的形状应该“可广播”为原始数组的形状。默认值:(
None
所有数组值均有效)- 偏移量(可选)
数组数据区域的整数偏移量。仅当数据是对象
None
或返回对象时才能使用此功能buffer
。默认:
0
。- 版本(必填)
显示接口版本的整数(即该版本为 3)。请小心,不要使用它来使暴露未来版本接口的对象失效。
C 结构体访问#
这种数组接口方法允许仅使用一个属性查找和定义良好的 C 结构来更快地访问数组。
- 目的。__数组结构__ #
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_intptr_t *shape; /* A length-nd array of shape information */
Py_intptr_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)。最终标志
NPY_ARR_HAS_DESCR
(0x800) 指示该结构是否具有 arrdescr 字段。除非存在此标志,否则不应访问该字段。
- NPY_ARR_HAS_DESCR #
自 2006 年 6 月 16 日起新增:
在过go,大多数实现使用
(now ) 本身desc
的成员(不要将其与上面结构的“descr”成员混淆——它们是两个独立的东西)来保存指向公开接口的对象的指针。现在这是界面的显式部分。请务必在返回之前获取对该对象的引用并调用,并配置析构函数以 decref 该引用。PyCObject
PyCapsule
PyArrayInterface
PyCapsule_SetContext
PyCapsule
笔记
__array_struct__
被视为遗留代码,不应用于新代码。请改用buffer 协议或 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')]
应该清楚的是,任何结构化类型都可以使用此接口进行描述。
与 Array 接口(版本 2)的差异#
版本 2 的界面非常相似。差异主要是审美上的。尤其:
PyArrayInterface 结构末尾没有 descr 成员(因此没有标志 ARR_HAS_DESCR)
未指定返回的
context
的成员(PyCapsule
正式的desc
的成员)。通常,它是公开数组的对象(以便在 C 对象被销毁时可以保留并销毁对它的引用)。现在明确要求以某种方式使用该字段来保存对所属对象的引用。PyCObject
__array_struct__
笔记
直到 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__