数组接口协议#

备注

本页描述了 NumPy 特定的 API,用于从其他 C 扩展访问 NumPy 数组的内容. PEP 3118The Revised Buffer Protocol 引入了类似的,标准化的 API,供任何扩展模块使用. Cython 的缓冲区数组支持使用 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 ).请注意,这些整数可能大于平台 intlong 可以容纳的范围(Python int 是 C long ).使用此属性的代码需要适当地处理这种情况;或者在可能发生溢出时引发错误,或者使用 long long 作为形状的 C 类型.

typestr (必需)

一个字符串,提供同质数组的基本类型.基本字符串格式包含 3 个部分:一个描述数据字节序的字符( < :小端, > :大端, | :不相关),一个给出数组基本类型的字符代码,以及一个整数,提供该类型使用的字节数.

基本类型字符代码为:

t

位字段(后面的整数给出位字段中的位数).

b

布尔值(整数类型,其中所有值仅为 TrueFalse )

i

整数

u

无符号整数

f

浮点数

c

复数浮点数

m

Timedelta

M

Datetime

O

对象(即,内存包含指向 PyObject 的指针)

S

字符串(固定长度的 char 序列)

U

Unicode(固定长度的 Py_UCS4 序列)

V

其他(void – 每个项目都是固定大小的内存块)

descr (可选)

提供同质数组中每个项目的内存布局的更详细描述的元组列表.列表中的每个元组都有两个或三个元素.通常,当 typestr 为 V[0-9]+ 时,将使用此属性,但这不是必需的.唯一的要求是 typestr 键中表示的字节数与此处表示的总字节数相同.目的是支持构成数组元素的类 C 结构的描述.列表中的每个元组的元素是

  1. 一个字符串,提供与数据类型此部分关联的名称.这也可以是 ('full name', 'basic_name') 的元组,其中 basic name 将是表示字段全名的有效 Python 变量名.

  2. typestr 中的基本类型描述字符串或另一个列表(对于嵌套结构类型)

  3. 一个可选的 shape 元组,提供应重复此结构部分的次数.如果未给出,则假定不重复.可以使用此通用接口描述非常复杂的结构.但是请注意,数组的每个元素仍然具有相同的数据类型.下面给出了一些使用此接口的示例.

默认值: [('', typestr)]

data (可选)

一个 2 元组,其第一个参数是一个指向存储数组内容的 Python integer .

备注

当通过 PyLong_From* 或高级绑定(如 Cython 或 pybind11)从 C/C++ 转换时,请确保使用足够大的位宽的整数.

此指针必须指向数据的第一个元素(换句话说,在这种情况下总是忽略任何偏移量).元组中的第二个条目是一个只读标志(true 表示数据区域是只读的).

此属性也可以是一个暴露 buffer interface 的对象,该接口将用于共享数据.如果此键不存在(或返回 None),则将通过对象本身的 buffer 接口完成内存共享.在这种情况下,offset 键可用于指示缓冲区的起始位置.如果内存区域要被保护,那么必须由新对象存储对暴露 array 接口的对象的引用.

默认值: None

strides (可选)

可以是 None (表示 C 风格的连续数组),也可以是一个 strides 元组,该元组提供跳转到相应维度中的下一个数组元素所需的字节数.每个条目必须是一个整数(一个 Python int ).与 shape 一样,这些值可能大于 C intlong 可以表示的值;调用代码应适当处理此问题,可以引发错误,也可以在 C 中使用 long long .默认值为 None ,这意味着 C 风格的连续内存缓冲区.在此模型中,数组的最后一个维度变化最快.例如,数组条目为 8 字节长且 shape 为 (10, 20, 30) 的对象的默认 strides 元组将为 (4800, 240, 8) .

默认值: None (C 风格的连续)

mask (可选)

None 或一个暴露 array 接口的对象.mask 数组的所有元素应仅解释为 true 或 not true,指示此数组的哪些元素有效.此对象的 shape 应该是 “broadcastable” 到原始数组的 shape.

默认值: 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).最后一个标志 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 接口非常相似. 差异主要在于美学方面. 特别是:

  1. PyArrayInterface 结构在末尾没有 descr 成员(因此没有 ARR_HAS_DESCR 标志)

  2. __array_struct__ 返回的 PyCapsulecontext 成员(正式地说是 PyCObjectdesc 成员)未指定. 通常,它是公开数组的对象(以便可以保留对其的引用并在销毁 C 对象时将其销毁). 现在明确要求以某种方式使用此字段来保存对所有者对象的引用.

    备注

    截至 2020 年 8 月,此内容显示:

    现在它必须是一个元组,其第一个元素是带有 “PyArrayInterface Version #” 的字符串,其第二个元素是公开数组的对象.

    此设计在提出后几乎立即被撤回,在 <https://mail.python.org/pipermail/numpy-discussion/2006-June/020995.html> 中. 尽管有 14 年的文档与此相反,但在任何时候假设 __array_interface__ 胶囊都持有此元组内容都是无效的.

  3. __array_interface__['data'] 返回的元组曾经是一个十六进制字符串(现在它是一个整数或长整数).

  4. 没有 __array_interface__ 属性,而是 __array_interface__ 字典中的所有键(版本除外)都是它们自己的属性:因此,要获得 Python 端的信息,您必须单独访问属性:

    • __array_data__

    • __array_shape__

    • __array_strides__

    • __array_typestr__

    • __array_descr__

    • __array_offset__

    • __array_mask__