Интерфейс массива
Примечание
Эта страница описывает специфичный для numpy API для доступа к содержимому массива numpy из других расширений C. PEP 3118 – The Revised Buffer Protocol представляет подобный стандартизированный API для Python 2.6 и 3.0, который любой модуль расширения может использовать. Cython использует API PEP 3118 для поддержки буферизованных массивов; см. учебник по numpy для Cython. 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Строка (последовательность символов фиксированной длины)
UUnicode (последовательность
Py_UNICODEфиксированной длины)VДругой (void * — каждый элемент — фиксированный блок памяти)
- descr (необязательно)
-
Список кортежей, предоставляющих более подробное описание структуры памяти для каждого элемента однородного массива. Каждый кортеж в списке содержит два или три элемента. Обычно этот атрибут используется, когда typestr это
V[0-9]+, но это не обязательно. Единственное требование — количество байтов, представленных в ключе typestr, должно быть таким же, как и общее количество байтов, представленных здесь. Идея заключается в поддержке описаний структур C, которые составляют элементы массива. Элементы каждого кортежа в списке:- Строка, предоставляющая имя, связанное с этой частью типа данных. Это также может быть кортеж из
('full name', 'basic_name'), где основное имя будет допустимым именем переменной Python, представляющим полное имя поля. - Описание базового типа, как в typestr, или другой список (для вложенных структурированных типов)
- Необязательный кортеж размеров, указывающий, сколько раз эта часть структуры должна повторяться. По умолчанию повторений нет, если это не задано. С помощью этого универсального интерфейса можно описать очень сложные структуры. Обратите внимание, что каждый элемент массива все равно имеет один и тот же тип данных.
Значение по умолчанию:
[('', typestr)] - Строка, предоставляющая имя, связанное с этой частью типа данных. Это также может быть кортеж из
- data (необязательно)
-
Двухэлементный кортеж, первый элемент которого — целое число (длинное целое число, если необходимо), указывающее на область данных, хранящую содержимое массива. Этот указатель должен указывать на первый элемент данных (иными словами, любые смещения игнорируются в этом случае). Второй элемент кортежа — флаг только для чтения (True означает, что область данных только для чтения).
Этот атрибут также может быть объектом, предоставляющим интерфейс буфера, который будет использоваться для совместного использования данных. Если этот ключ отсутствует (или возвращает 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 или объект, предоставляющий интерфейс массива. Все элементы массива маски должны интерпретироваться только как истинные или ложные, указывая, какие элементы этого массива действительны. Размер этого объекта должен быть
“broadcastable”к размеру исходного массива.Значение по умолчанию: None (все значения массива действительны)
- offset (необязательно)
-
Смещение в области данных массива. Это может быть использовано только когда data это
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 и настройте деструктор для уменьшения ссылки.
Примеры описания типа
Для ясности полезно привести несколько примеров описания типа и соответствующих записей «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__, вместо этого все ключи (кроме version) в словаре__array_interface__были своими собственными атрибутами: Таким образом, для получения информации со стороны Python вам приходилось отдельно обращаться к атрибутам:__array_data____array_shape____array_strides____array_typestr____array_descr____array_offset____array_mask__
© 2005–2022 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.21/reference/arrays.interface.html