Интерфейс массива
Примечание
Эта страница описывает 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__.
-
__array_interface__ -
Словарь элементов (3 обязательных и 5 необязательных). Для необязательных ключей в словаре используются неявные значения по умолчанию, если они не указаны.
Ключи:
shape (обязательно)
Кортеж, элементы которого представляют размер массива в каждой размерности. Каждый элемент — целое число (Python int или long). Обратите внимание, что эти целые числа могут быть больше, чем может вместить платформа «int» или «long» (Python int — это C long). Ответственный за использование этого атрибута код должен корректно обрабатывать это; либо с помощью исключения при возможном переполнении, либо с использованием
Py_LONG_LONGкак типа C для размеров.typestr (обязательно)
Строка, содержащая базовый тип однородного массива. Базовый формат строки состоит из 3 частей: символ, описывающий порядок байтов данных (
<: little-endian,>: big-endian,|: нерелевантно), символ, описывающий базовый тип массива, и целое число, представляющее количество байтов, используемых типом.Символы базовых типов:
tПоле бита (следующее целое число указывает количество битов в поле бита).
bБулево (целый тип, где все значения — только True или False)
iЦелое число
uБеззнаковое целое число
fЧисло с плавающей точкой
cКомплексное число с плавающей точкой
mTimedelta
MDatetime
OОбъект (т. е. память содержит указатель на
PyObject)SСтрока (последовательность символов фиксированной длины)
UUnicode (последовательность
Py_UNICODEфиксированной длины)VДругое (void * — каждый элемент — блок памяти фиксированного размера)
descr (необязательно)
Список кортежей, предоставляющий более подробное описание структуры памяти для каждого элемента однородного массива. Каждый кортеж в списке содержит два или три элемента. Обычно этот атрибут используется, когда typestr равен
V[0-9]+, но это не обязательно. Единственное требование состоит в том, что количество байтов, представленных в ключе typestr, должно быть таким же, как общее количество байтов, представленных здесь. Идея состоит в том, чтобы поддерживать описания похожих на C структур, которые составляют элементы массива. Элементы каждого кортежа в списке:- Строка, содержащая имя, связанное с этой частью типа данных. Это также может быть кортеж из
('full name', 'basic_name'), где базовое имя будет допустимым именем переменной Python, представляющим полное имя поля. - Описание базового типа, как в typestr, или другой список (для вложенных структурированных типов)
- Необязательный кортеж shape, указывающий, сколько раз эта часть структуры должна повторяться. По умолчанию повторения не предполагаются, если это не указано. С помощью этого универсального интерфейса можно описывать очень сложные структуры. Однако обратите внимание, что каждый элемент массива по-прежнему имеет одинаковый тип данных. Некоторые примеры использования этого интерфейса приведены ниже.
Default:
[('', typestr)]data (необязательно)
Двухэлементный кортеж, первый аргумент которого — целое число (целое число long, если необходимо), указывающее на область данных, хранящую содержимое массива. Этот указатель должен указывать на первый элемент данных (другими словами, любой смещение в этом случае игнорируется). Второй элемент кортежа — флаг только для чтения (истина означает, что область данных недоступна для записи).
Этот атрибут также может быть объектом, предоставляющим
buffer interface, который будет использоваться для совместного использования данных. Если этот ключ отсутствует (или возвращает None), совместное использование памяти выполняется через интерфейс буферизации объекта. В этом случае смещение ключа можно использовать для указания начала буфера. Ссылка на объект, предоставляющий интерфейс массива, должна храниться в новом объекте, если область памяти необходимо закрепить.Default: None
strides (необязательно)
None для указания непрерывного массива по схеме C или кортеж шагов, который указывает количество байтов, необходимых для перехода к следующему элементу массива в соответствующей размерности. Каждый элемент должен быть целым числом (Python
intилиlong). Как и в случае с shape, значения могут быть больше, чем может представлять C «int» или «long»; вызывающий код должен должным образом обработать это, либо с помощью исключения, либо с использованиемPy_LONG_LONGв C. Значение по умолчанию — None, что подразумевает непрерывный буфер памяти по схеме C. В этой модели последняя размерность массива изменяется быстрее всего. Например, кортеж шагов по умолчанию для объекта, элементы массива которого имеют длину 8 байт, а размер массива — (10,20,30), будет (4800, 240, 8).Default: None (непрерывный по схеме C)
mask (необязательно)
None или объект, предоставляющий интерфейс массива. Все элементы массива маски должны интерпретироваться только как истинные или не истинные, показывая, какие элементы этого массива допустимы. Размер этого объекта должен быть
“broadcastable”относительно размера исходного массива.Default: None (все значения массива допустимы)
offset (необязательно)
Смещение в области данных массива. Это можно использовать только тогда, когда данные равны None или возвращают объект
buffer.Default: 0.
version (обязательно)
Целое число, показывающее версию интерфейса (т. е. 3 для этой версии). Будьте осторожны, не используйте это для отмены действия объектов, предоставляющих будущие версии интерфейса.
- Строка, содержащая имя, связанное с этой частью типа данных. Это также может быть кортеж из
Доступ к C-структурам
Этот подход к интерфейсу массива позволяет быстрее получать доступ к массиву, используя только один поиск атрибута и хорошо определенную структуру C.
-
__array_struct__ -
:c:type:
PyCObject, члены которого содержат указатель на заполненную структуруPyArrayInterface. Память для структуры создается динамически, и для нее создается соответствующий деструктор, поэтому получателю этого атрибута достаточно применить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;
Поле флагов может состоять из 5 битов, показывающих, как должны интерпретироваться данные, и одного бита, показывающего, как должен интерпретироваться интерфейс. Битами данных являются CONTIGUOUS (0x1), FORTRAN (0x2), ALIGNED (0x100), NOTSWAPPED (0x200) и WRITEABLE (0x400). Конечный флаг ARR_HAS_DESCR (0x800) указывает, присутствует ли поле arrdescr.
Добавлен начиная с 16 июня 2006 г.:
В прошлом большинство реализаций использовали член «desc» самого PyCObject (не путать с членом «descr» структуры PyArrayInterface выше — это разные вещи) для хранения указателя на объект, предоставляющий интерфейс. Теперь это явная часть интерфейса. Убедитесь, что у вас есть ссылка на объект при создании PyCObject с помощью PyCObject_FromVoidPtrAndDesc.
Примеры описания типов
Для большей ясности полезно привести несколько примеров описания типа и соответствующих записей «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)
- Член desc объекта PyCObject, возвращаемого из __array_struct__, не был указан. Обычно это был объект, экспонирующий массив (чтобы можно было сохранить ссылку на него и уничтожить при уничтожении объекта C). Теперь это должна быть кортеж, первый элемент которого — строка с «PyArrayInterface Версия #», а второй — объект, экспонирующий массив.
- Кортеж, возвращаемый из __array_interface__[‘data’], использовался как шестнадцатеричная строка (теперь это целое или длинное целое число).
-
Атрибут __array_interface__ отсутствовал, вместо него все ключи (кроме версии) в словаре __array_interface__ были собственными атрибутами: Таким образом, для получения информации со стороны Python необходимо было отдельно обращаться к атрибутам:
- __array_data__
- __array_shape__
- __array_strides__
- __array_typestr__
- __array_descr__
- __array_offset__
- __array_mask__
© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.19/reference/arrays.interface.html