> **Источник:** https://python-all.ru/3.2/c-api/buffer.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# Протокол буфера

Некоторые объекты в Python предоставляют доступ к лежащему в основе массиву памяти или *буферу*. К таким объектам относятся встроенные [`bytes`](https://python-all.ru/3.2/library/functions.html#bytes) и [`bytearray`](https://python-all.ru/3.2/library/functions.html#bytearray), а также некоторые типы расширений, например [`array.array`](https://python-all.ru/3.2/library/array.html#array.array). Сторонние библиотеки могут определять собственные типы для специальных целей, таких как обработка изображений или численный анализ.

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

Python предоставляет такую возможность на уровне C в виде [*буферного протокола*](https://python-all.ru/3.2/c-api/buffer.html#bufferobjects). У этого протокола есть две стороны:

- На стороне производителя тип может экспортировать «буферный интерфейс», который позволяет объектам этого типа предоставлять информацию о своём базовом буфере. Этот интерфейс описан в разделе [*Buffer Object Structures*](https://python-all.ru/3.2/c-api/typeobj.html#buffer-structs);
- На стороне потребителя доступно несколько способов получить указатель на необработанные базовые данные объекта (например, параметр метода).

Простые объекты, такие как [`bytes`](https://python-all.ru/3.2/library/functions.html#bytes) и [`bytearray`](https://python-all.ru/3.2/library/functions.html#bytearray), предоставляют свой нижележащий буфер в байт-ориентированной форме. Возможны и другие формы; например, элементы, предоставляемые [`array.array`](https://python-all.ru/3.2/library/array.html#array.array), могут быть многобайтовыми значениями.

Примером потребителя интерфейса буфера является метод [`write()`](https://python-all.ru/3.2/library/io.html#io.BufferedIOBase.write) файловых объектов: любой объект, способный экспортировать последовательность байтов через интерфейс буфера, может быть записан в файл. В то время как `write()` требует только доступа на чтение к внутреннему содержимому переданного ему объекта, другие методы, такие как [`readinto()`](https://python-all.ru/3.2/library/io.html#io.BufferedIOBase.readinto), требуют доступа на запись к содержимому своего аргумента. Интерфейс буфера позволяет объектам выборочно разрешать или запрещать экспорт буферов для чтения-записи и только для чтения.

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

- вызов [`PyObject_GetBuffer()`](https://python-all.ru/3.2/c-api/buffer.html#PyObject_GetBuffer) с правильными параметрами;
- вызов [`PyArg_ParseTuple()`](https://python-all.ru/3.2/c-api/arg.html#PyArg_ParseTuple) (или одной из его разновидностей) с одним из `y*`, `w*` или `s*` [*кодов формата*](https://python-all.ru/3.2/c-api/arg.html#arg-parsing).

В обоих случаях [`PyBuffer_Release()`](https://python-all.ru/3.2/c-api/buffer.html#PyBuffer_Release) необходимо вызвать, когда буфер больше не нужен. Несоблюдение этого может привести к различным проблемам, таким как утечка ресурсов.

## Структура буфера

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

В отличие от большинства типов данных, предоставляемых интерпретатором Python, буферы не являются указателями на [`PyObject`](https://python-all.ru/3.2/c-api/structures.html#PyObject), а представляют собой простые структуры C. Это позволяет создавать и копировать их очень просто. Когда требуется универсальная обёртка вокруг буфера, можно создать объект [*memoryview*](https://python-all.ru/3.2/c-api/memoryview.html#memoryview-objects).

**`Py_buffer`**

**void \*`buf`**

Указатель на начало памяти объекта.

**Py\_ssize\_t `len`**

Общая длина памяти в байтах.

**int `readonly`**

Индикатор того, является ли буфер доступным только для чтения.

**const char \*`format`**

Строка, завершающаяся *NULL*, в синтаксисе модуля [`struct`](https://python-all.ru/3.2/library/struct.html#module-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`. Если эти значения смещений больше или равны 0, то значение, хранящееся вдоль указанного измерения, является указателем, а значение смещения указывает, сколько байтов нужно добавить к указателю после разыменования. Отрицательное значение смещения означает, что разыменование не требуется (шаг в непрерывном блоке памяти).

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

```c
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()`](https://python-all.ru/3.2/c-api/buffer.html#PyBuffer_SizeFromFormat), однако экспортер может знать эту информацию без разбора строки формата, и знание размера элемента необходимо для правильной интерпретации шагов. Поэтому его хранение более удобно и быстрее.

**void \*`internal`**

Это поле предназначено для внутреннего использования экспортирующим объектом. Например, оно может быть переинтерпретировано как целое число экспортером и использоваться для хранения флагов о том, нужно ли освобождать массивы shape, strides и suboffsets при освобождении буфера. Потребитель никогда не должен изменять это значение.

## Функции, связанные с буфером

#### `int PyObject_CheckBuffer(PyObject *obj)`

Возвращает 1, если *obj* поддерживает интерфейс буфера, иначе 0. Если возвращается 1, это не гарантирует, что [`PyObject_GetBuffer()`](https://python-all.ru/3.2/c-api/buffer.html#PyObject_GetBuffer) выполнится успешно.

#### `int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags)`

Экспортирует представление некоторых внутренних данных целевого объекта *obj*. *obj* не должен быть NULL, а *view* должен указывать на существующую структуру [`Py_buffer`](https://python-all.ru/3.2/c-api/buffer.html#Py_buffer), выделенную вызывающим (в большинстве случаев эта функция просто объявляет локальную переменную типа [`Py_buffer`](https://python-all.ru/3.2/c-api/buffer.html#Py_buffer)). Аргумент *flags* – это битовое поле, указывающее, какой тип буфера запрашивается. Интерфейс буфера допускает сложные варианты расположения памяти; однако некоторые вызывающие не захотят обрабатывать всю сложность и вместо этого запросят простое представление целевого объекта (используя [`PyBUF_SIMPLE`](https://python-all.ru/3.2/c-api/buffer.html#PyBUF_SIMPLE) для представления только для чтения и [`PyBUF_WRITABLE`](https://python-all.ru/3.2/c-api/buffer.html#PyBUF_WRITABLE) для представления для чтения и записи).

Некоторые экспортеры могут не иметь возможности предоставить память любым способом и могут вызывать ошибки, чтобы сообщить некоторым потребителям, что что-то невозможно. Эти ошибки должны быть [`BufferError`](https://python-all.ru/3.2/library/exceptions.html#BufferError), если только нет другой ошибки, которая на самом деле является причиной проблемы. Экспортер может использовать информацию о флагах, чтобы упростить, какая часть структуры [`Py_buffer`](https://python-all.ru/3.2/c-api/buffer.html#Py_buffer) заполняется нестандартными значениями, и/или вызвать ошибку, если объект не поддерживает более простое представление своей памяти.

В случае успеха возвращается 0, а структура *view* заполняется полезными значениями. В случае ошибки возвращается -1 и возбуждается исключение; структура *view* остаётся в неопределённом состоянии.

Ниже перечислены возможные значения аргумента *flags*.

**`PyBUF_SIMPLE`**

Это флаг по умолчанию. Возвращаемый буфер предоставляет область памяти только для чтения. Предполагается, что формат данных – необработанные беззнаковые байты, без какой-либо определённой структуры. Это «автономная» константа флага. Её никогда не нужно объединять с другими через ‘|’. Экспортер вызовет ошибку, если не сможет предоставить такой непрерывный байтовый буфер.

**`PyBUF_WRITABLE`**

Как [`PyBUF_SIMPLE`](https://python-all.ru/3.2/c-api/buffer.html#PyBUF_SIMPLE), но возвращаемый буфер доступен для записи. Если экспортер не поддерживает буферы для записи, возникает ошибка.

**`PyBUF_STRIDES`**

Это подразумевает [`PyBUF_ND`](https://python-all.ru/3.2/c-api/buffer.html#PyBUF_ND). Возвращаемый буфер должен предоставлять информацию о шагах (т.е. шаги не могут быть NULL). Это используется, когда потребитель может работать со страйдед-массивами (несмежными по измерениям). Обработка шагов автоматически подразумевает возможность обработки формы. Экспортер может вызвать ошибку, если страйдед-представление данных невозможно (например, без подсмещений).

**`PyBUF_ND`**

Возвращаемый буфер должен предоставлять информацию о форме. Память будет считаться непрерывной в стиле C (последнее измерение изменяется быстрее всего). Экспортер может вызвать ошибку, если не может предоставить такой непрерывный буфер. Если этот флаг не задан, форма будет *NULL*.

**`PyBUF_C_CONTIGUOUS`**

**`PyBUF_F_CONTIGUOUS`**

**`PyBUF_ANY_CONTIGUOUS`**

Эти флаги указывают, что возвращаемый буфер должен быть соответственно C-непрерывным (последнее измерение изменяется быстрее всего), Fortran-непрерывным (первое измерение изменяется быстрее всего) или любым из них. Все эти флаги подразумевают [`PyBUF_STRIDES`](https://python-all.ru/3.2/c-api/buffer.html#PyBUF_STRIDES) и гарантируют, что структура информации о шагах буфера будет заполнена корректно.

**`PyBUF_INDIRECT`**

Этот флаг указывает, что возвращаемый буфер должен содержать информацию о подсмещениях (может быть NULL, если подсмещения не нужны). Это может использоваться, когда потребитель может обрабатывать косвенную ссылку на массив, подразумеваемую этими подсмещениями. Этот флаг подразумевает [`PyBUF_STRIDES`](https://python-all.ru/3.2/c-api/buffer.html#PyBUF_STRIDES).

**`PyBUF_FORMAT`**

Возвращаемый буфер должен содержать настоящую информацию о формате, если этот флаг передан. Это используется, когда потребитель собирается проверять, какой «тип» данных на самом деле хранится. Экспортер всегда должен предоставлять эту информацию по запросу. Если формат явно не запрошен, формат должен быть возвращен как *NULL* (что означает `'B'`, или беззнаковые байты).

**`PyBUF_STRIDED`**

Это эквивалентно `(PyBUF_STRIDES | PyBUF_WRITABLE)`.

**`PyBUF_STRIDED_RO`**

Это эквивалентно `(PyBUF_STRIDES)`.

**`PyBUF_RECORDS`**

Это эквивалентно `(PyBUF_STRIDES | PyBUF_FORMAT | PyBUF_WRITABLE)`.

**`PyBUF_RECORDS_RO`**

Это эквивалентно `(PyBUF_STRIDES | PyBUF_FORMAT)`.

**`PyBUF_FULL`**

Это эквивалентно `(PyBUF_INDIRECT | PyBUF_FORMAT | PyBUF_WRITABLE)`.

**`PyBUF_FULL_RO`**

Это эквивалентно `(PyBUF_INDIRECT | PyBUF_FORMAT)`.

**`PyBUF_CONTIG`**

Это эквивалентно `(PyBUF_ND | PyBUF_WRITABLE)`.

**`PyBUF_CONTIG_RO`**

Это эквивалентно `(PyBUF_ND)`.

#### `void PyBuffer_Release(Py_buffer *view)`

Освобождает буфер *view*. Эту функцию следует вызывать, когда буфер больше не используется, так как она может освободить память из него.

#### `Py_ssize_t PyBuffer_SizeFromFormat(const char *)`

Возвращает подразумеваемый [`itemsize`](https://python-all.ru/3.2/c-api/buffer.html#Py_buffer.itemsize) из строки формата struct `format`.

#### `int PyBuffer_IsContiguous(Py_buffer *view, char fortran)`

Возвращает 1, если память, определяемая *view*, является смежной в стиле C (*fortran* равно `'C'`) или в стиле Fortran (*fortran* равно `'F'`), или любого из них (*fortran* равно `'A'`). В противном случае возвращает 0.

#### `void PyBuffer_FillContiguousStrides(int ndim, Py_ssize_t *shape, Py_ssize_t *strides, Py_ssize_t itemsize, char fortran)`

Заполняет массив *strides* байтовыми шагами смежного массива (в стиле C, если *fortran* равно `'C'`, или в стиле Fortran, если *fortran* равно `'F'`) массива заданной формы с заданным количеством байт на элемент.

#### `int PyBuffer_FillInfo(Py_buffer *view, PyObject *obj, void *buf, Py_ssize_t len, int readonly, int infoflags)`

Заполняет структуру буферной информации *view* корректно для экспортёра, который может передавать только смежный блок памяти из «unsigned bytes» заданной длины. Возвращает 0 при успехе и -1 (с порождением исключения) при ошибке.
