Интерфейс массива
Примечание
Эта страница описывает специфический для numpy API для доступа к содержимому массива numpy из других расширений C. PEP 3118 – The Revised Buffer Protocol вводит аналогичный стандартизованный API для модулей расширения Python 2.6 и 3.0. Поддержка буферных массивов в Cython использует API PEP 3118; см. учебник Cython по numpy. Cython предоставляет способ написания кода, поддерживающего протокол буфера с версиями Python, более старыми чем 2.6, так как он имеет обратную совместимую реализацию, использующую интерфейс массива, описанный здесь.
- версия
-
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Строка (последовательность символов фиксированной длины)
UЮникод (последовательность
Py_UNICODEфиксированной длины)VДругое (void * – каждый элемент – фиксированный блок памяти)
- descr (необязательно)
-
Список кортежей, предоставляющий более подробное описание структуры памяти для каждого элемента в однородном массиве. Каждый кортеж в списке содержит два или три элемента. Обычно этот атрибут используется, когда typestr это
V[0-9]+, но это не требование. Единственное требование – количество байтов, представленных в ключе typestr, должно быть таким же, как общее количество байтов, представленных здесь. Идея в том, чтобы поддерживать описания C-подобных структур, составляющих элементы массива. Элементами каждого кортежа в списке являются- Строка, предоставляющая имя, связанное с этой частью типа данных. Это также может быть кортеж из
('full name', 'basic_name')где базовое имя является допустимым именем Python-переменной, представляющей полное имя поля. - Либо строковое описание базового типа, как в typestr, либо другой список (для вложенных структурных типов)
- Необязательный кортеж размеров, показывающий, сколько раз эта часть структуры должна повторяться. Если это не указано, предполагается, что повторений нет. Очень сложные структуры могут быть описаны с помощью этого общего интерфейса. Обратите внимание, что каждый элемент массива по-прежнему имеет один и тот же тип данных.
Значение по умолчанию:
[('', typestr)] - Строка, предоставляющая имя, связанное с этой частью типа данных. Это также может быть кортеж из
- data (необязательно)
-
2-кортеж, первым аргументом которого является целое число (длинное целое число, если необходимо), указывающее на область данных, хранящую содержимое массива. Этот указатель должен указывать на первый элемент данных (другими словами, любой смещение здесь игнорируется). Второй элемент кортежа – флаг только для чтения (истинное значение означает, что область данных только для чтения).
Этот атрибут также может быть объектом, экспонирующим интерфейс буфера, который будет использоваться для совместного использования данных. Если этот ключ отсутствует (или возвращает None), то совместное использование памяти будет осуществляться через интерфейс буфера самого объекта. В этом случае ключ offset может использоваться для указания начала буфера. Ссылка на объект, экспонирующий интерфейс массива, должна храниться новым объектом, если область памяти должна быть защищена.
Значение по умолчанию: None
- strides (необязательно)
-
Либо
Noneдля указания C-подобного контигуального массива, либо кортеж смещений, который предоставляет количество байтов, необходимых для перехода к следующему элементу массива в соответствующей размерности. Каждый элемент должен быть целым числом (Pythonint). Как и с shape, значения могут быть больше, чем может представить Cintилиlong; вызывающий код должен правильно обработать это, либо путём выдачи ошибки, либо с использованиемlong longв C. Значение по умолчаниюNone, что подразумевает C-подобный контигуальный буфер памяти. В этой модели последняя размерность массива изменяется быстрее всего. Например, кортеж смещений по умолчанию для объекта, чьи элементы массива имеют длину 8 байт и чьё shape –(10, 20, 30)будет(4800, 240, 8)Значение по умолчанию:
None(C-подобный контигуальный) - mask (необязательно)
-
None или объект, экспонирующий интерфейс массива. Все элементы маски массива должны интерпретироваться только как истинные или ложные, указывая, какие элементы этого массива являются допустимыми. Форма этого объекта должна быть
“broadcastable”к форме исходного массива.Значение по умолчанию: None (все значения массива допустимы)
- offset (необязательно)
-
Целое смещение в области данных массива. Это может использоваться только тогда, когда данные
Noneили возвращают объектbufferЗначение по умолчанию: 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_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 битов, показывающих, как следует интерпретировать данные, и одного бита, показывающего, как следует интерпретировать интерфейс. Битами данных являются 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_interface__ ‘descr’. Благодарим Скотта Гилберта за эти примеры:
В каждом случае ключ ‘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–2021 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.20/reference/arrays.interface.html