Документация Python неофициальный перевод
Содержание страницы

Протокол буфераBuffer Protocol

Некоторые объекты в Python предоставляют доступ к лежащему в основе массиву памяти или буферу. К таким объектам относятся встроенные bytes и bytearray, а также некоторые типы расширений, например array.array. Сторонние библиотеки могут определять собственные типы для специальных целей, таких как обработка изображений или численный анализ.

Хотя каждый из этих типов имеет свою семантику, их объединяет то, что они основаны на возможно большом буфере памяти. В некоторых ситуациях желательно иметь прямой доступ к этому буферу без промежуточного копирования.

Python предоставляет такую возможность на уровне C в виде протокола буфера. У этого протокола две стороны:

  • На стороне производителя тип может экспортировать «буферный интерфейс», который позволяет объектам этого типа предоставлять информацию о своём базовом буфере. Этот интерфейс описан в разделе Buffer Object Structures;
  • На стороне потребителя доступно несколько способов получить указатель на необработанные базовые данные объекта (например, параметр метода).

Простые объекты, такие как bytes и bytearray, предоставляют свой нижележащий буфер в байт-ориентированной форме. Возможны и другие формы; например, элементы, предоставляемые array.array, могут быть многобайтовыми значениями.

Примером потребителя интерфейса буфера является метод write() файловых объектов: любой объект, способный экспортировать последовательность байтов через интерфейс буфера, может быть записан в файл. В то время как write() требует только доступа на чтение к внутреннему содержимому переданного ему объекта, другие методы, такие как readinto(), требуют доступа на запись к содержимому своего аргумента. Интерфейс буфера позволяет объектам выборочно разрешать или запрещать экспорт буферов для чтения-записи и только для чтения.

Существует два способа для потребителя буферного интерфейса получить буфер над целевым объектом:

В обоих случаях PyBuffer_Release() необходимо вызвать, когда буфер больше не нужен. Несоблюдение этого может привести к различным проблемам, таким как утечка ресурсов.

Структура буфераThe buffer structure

Структуры буферов (или просто «буферы») полезны как способ предоставления бинарных данных из другого объекта программисту на Python. Их также можно использовать как механизм среза с нулевым копированием. Благодаря возможности ссылаться на блок памяти, можно довольно легко предоставить любые данные программисту на Python. Память может быть большим константным массивом в C-расширении, необработанным блоком памяти для манипуляций перед передачей в библиотеку операционной системы или использоваться для передачи структурированных данных в их родном формате в памяти.

В отличие от большинства типов данных, предоставляемых интерпретатором Python, буферы не являются указателями на PyObject, а представляют собой простые структуры C. Это позволяет создавать и копировать их очень просто. Когда требуется универсальная обёртка вокруг буфера, можно создать объект memoryview.

Py_buffer
void *buf
Указатель на начало памяти объекта.
Py_ssize_t len
Общая длина памяти в байтах.
int readonly
Индикатор того, является ли буфер доступным только для чтения.
const char *format
Строка, завершающаяся NULL, в синтаксисе модуля struct, описывающая содержимое элементов, доступных через буфер. Если это NULL, то подразумевается "B" (беззнаковые байты).
int ndim
Количество измерений, которые память представляет как многомерный массив. Если оно равно 0, strides и suboffsets должны быть NULL.
Py_ssize_t *shape
Массив Py_ssize_t длиной ndim, задающий форму памяти как многомерного массива. Обратите внимание, что ((*shape)[0] * ... * (*shape)[ndims-1])*itemsize должно быть равно len.
Py_ssize_t *strides
Массив Py_ssize_t длиной ndim, задающий количество байтов, которое нужно пропустить, чтобы перейти к новому элементу в каждом измерении.
Py_ssize_t *suboffsets

Массив Py_ssize_t длиной ndim. Если эти числа suboffset больше или равны 0, то значение, хранящееся по указанному измерению, является указателем, а значение suboffset определяет, сколько байтов добавить к указателю после разыменования. Отрицательное значение suboffset означает, что разыменование не должно выполняться (шаг в непрерывном блоке памяти).

Ниже приведена функция, возвращающая указатель на элемент в N-мерном массиве, на который указывает N-мерный индекс, при наличии как ненулевых шагов (strides), так и субсмещений (suboffsets):

void *get_item_pointer(int ndim, void *buf, Py_ssize_t *strides,
    Py_ssize_t *suboffsets, Py_ssize_t *indices) {
    char *pointer = (char*)buf;
    int i;
    for (i = 0; i < ndim; i++) {
        pointer += strides[i] * indices[i];
        if (suboffsets[i] >=0 ) {
            pointer = *((char**)pointer) + suboffsets[i];
        }
    }
    return (void*)pointer;
 }
Py_ssize_t itemsize
Это поле хранит размер элемента (в байтах) для каждого элемента общей памяти. Технически оно не обязательно, так как его можно получить с помощью PyBuffer_SizeFromFormat(), однако экспортер может знать эту информацию без разбора строки формата, и знание размера элемента необходимо для правильной интерпретации шагов. Поэтому его хранение более удобно и быстрее.
void *internal
Это поле предназначено для внутреннего использования экспортирующим объектом. Например, оно может быть переинтерпретировано как целое число экспортером и использоваться для хранения флагов о том, нужно ли освобождать массивы shape, strides и suboffsets при освобождении буфера. Потребитель никогда не должен изменять это значение.