Протокол интерфейса массива
Примечание
Эта страница описывает API NumPy для доступа к содержимому массива NumPy из других расширений C. PEP 3118 – The Revised Buffer Protocol представляет аналогичный, стандартизированный API для Python 2.6 и 3.0, который может использовать любой модуль расширения. Поддержка буферных массивов Cython использует API PEP 3118; см. учебник Cython по NumPy. Cython предоставляет способ написания кода, поддерживающего протокол буфера с версиями Python, более старыми, чем 2.6, так как он имеет обратную совместимую реализацию, использующую интерфейс массива, описанный здесь.
- version:
-
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 частей: символ, описывающий порядок байтов данных (
<: little-endian,>: big-endian,|: не относится), символ кода, задающий базовый тип массива, и целое число, указывающее количество байтов, используемых типом.Основные символы типов:
tБитовое поле (следующее целое число указывает количество битов в битовом поле).
bБулево (целый тип, где все значения являются только
TrueилиFalse)iЦелое число
uБеззнаковое целое число
fЧисло с плавающей точкой
cКомплексное число с плавающей точкой
mПродолжительность
MДата
OОбъект (т. е. память содержит указатель на
PyObject)SСтрока (фиксированная длина последовательности символов)
UUnicode (фиксированная длина последовательности
Py_UCS4)VДругое (void * — каждый элемент — фиксированный блок памяти)
- descr (необязательно)
-
Список кортежей, предоставляющий более подробное описание структуры памяти для каждого элемента в однородном массиве. Каждый кортеж в списке имеет два или три элемента. Обычно этот атрибут используется, когда typestr является
V[0-9]+, но это не обязательно. Единственное требование заключается в том, что количество байтов, представленных в ключе typestr, должно быть таким же, как общее количество байтов, представленных здесь. Идея состоит в том, чтобы поддержать описания C-подобных структур, которые образуют элементы массива. Элементы каждого кортежа в списке:- Строка, предоставляющая имя, связанное с этой частью типа данных. Это также может быть кортеж из
('full name', 'basic_name'), где базовое имя будет допустимым именем переменной Python, представляющим полное имя поля. - Базовое описание типа данных, как в typestr, или другой список (для вложенных структурированных типов)
- Необязательный кортеж формы, определяющий, сколько раз эта часть структуры должна повторяться. Если это не указано, повторений не предполагается. С помощью этого обобщенного интерфейса можно описать очень сложные структуры. Однако обратите внимание, что каждый элемент массива по-прежнему имеет одинаковый тип данных.
Значение по умолчанию:
[('', typestr)] - Строка, предоставляющая имя, связанное с этой частью типа данных. Это также может быть кортеж из
- data (необязательно)
-
Двухэлементный кортеж, первый элемент которого — целое число Python, указывающее на область данных, хранящую содержимое массива.
Примечание
При преобразовании из C/C++ с помощью
PyLong_From*или высокоуровневых библиотек, таких как Cython или pybind11, убедитесь, что используется целое число с достаточной разрядностью.Этот указатель должен указывать на первый элемент данных (другими словами, любой смещение в этом случае игнорируется). Второй элемент кортежа — флаг только для чтения (истина означает, что область данных только для чтения).
Этот атрибут также может быть объектом, который экспонирует интерфейс буфера, который будет использоваться для совместного использования данных. Если этот ключ отсутствует (или возвращает None), то совместное использование памяти будет осуществляться через интерфейс буфера самого объекта. В этом случае ключ offset может использоваться для указания начала буфера. Ссылка на объект, экспонирующий интерфейс массива, должна быть сохранена новым объектом, если область памяти должна быть защищена.
Значение по умолчанию:
None - strides (необязательно)
-
Либо
Noneдля указания C-стильного непрерывного массива, либо кортеж смещений, который определяет количество байтов, необходимых для перехода к следующему элементу массива в соответствующей размерности. Каждый элемент должен быть целым числом (Pythonint). Как и в случае с shape, значения могут быть больше, чем может быть представлено Cintилиlong; вызывающий код должен должным образом обработать это, либо выбросив ошибку, либо используяlong longв C. Значение по умолчанию —None, что подразумевает непрерывный буфер памяти в стиле C. В этой модели последняя размерность массива изменяется быстрее всего. Например, кортеж смещений по умолчанию для объекта, чьи элементы массива имеют длину 8 байт, а форма —(10, 20, 30)будет(4800, 240, 8).Значение по умолчанию:
None(C-стиль) - mask (необязательно)
-
Noneили объект, экспонирующий интерфейс массива. Все элементы массива маски должны интерпретироваться только как истина или ложь, указывая, какие элементы этого массива являются допустимыми. Размеры этого объекта должны соответствовать размерности исходного массива.Значение по умолчанию:
None(Все значения массива допустимы) - offset (необязательно)
-
Целое смещение в области данных массива. Это может быть использовано только когда data —
Noneили возвращает объектmemoryview.Значение по умолчанию:
0. - version (обязательно)
-
Целое число, показывающее версию интерфейса (т. е. 3 для этой версии). Будьте осторожны, не используйте это для аннулирования объектов, экспонирующих будущие версии интерфейса.
Доступ к C-структуре
Этот подход к интерфейсу массива позволяет быстрее получить доступ к массиву, используя только один поиск атрибута и хорошо определённую C-структуру.
- object.__array_struct__
-
A
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 бит, показывающих, как данные должны интерпретироваться, и одного бита, показывающего, как должен интерпретироваться интерфейс. Битами данных являются 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
Новое с 16 июня 2006 года:
В прошлом большинство реализаций использовали поле desc структуры PyCObject (теперь PyCapsule) для хранения указателя на объект, экспонирующий интерфейс. Теперь это явная часть интерфейса. Убедитесь, что вы взяли ссылку на объект и вызвали PyCapsule_SetContext перед возвращением PyCapsule, и настройте деструктор для уменьшения ссылок на эту ссылку.
Примечание
__array_struct__ считается устаревшим и не должен использоваться в новом коде. Используйте протокол буфера или протокол DLPack numpy.from_dlpack вместо этого.
Примеры описания типов
Для большей ясности полезно привести несколько примеров описания типов и соответствующих записей «descr» для __array_interface__. Спасибо Скотту Гильберту за эти примеры:
В каждом случае ключ «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)
-
Поле
contextвPyCapsule(формально полеdescструктурыPyCObject), возвращаемое из__array_struct__, не было указано. Обычно это был объект, экспонирующий массив (чтобы можно было сохранить ссылку на него и уничтожить её при уничтожении C-объекта). Теперь явным требованием является использование этого поля для хранения ссылки на владеющий объект.Примечание
До августа 2020 года это было:
Теперь это должна быть кортеж, первый элемент которого — строка с «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__
© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/reference/arrays.interface.html