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

---

# Буферы и объекты memoryview

Объекты Python, реализованные на C, могут экспортировать группу функций, называемую «буферным интерфейсом». Эти функции могут использоваться объектом для предоставления своих данных в сыром, байт-ориентированном формате. Клиенты объекта могут использовать буферный интерфейс для прямого доступа к данным объекта без необходимости их предварительного копирования.

Два примера объектов, поддерживающих буферный интерфейс: строки и массивы. Строковый объект предоставляет символьное содержимое в байт-ориентированной форме буферного интерфейса. Массив может предоставлять своё содержимое только через старый буферный интерфейс. Это ограничение не распространяется на Python 3, где объекты [`memoryview`](https://python-all.ru/2.7/library/stdtypes.html#memoryview) также можно создавать из массивов. Элементы массива могут быть многобайтовыми значениями.

Примером использования буферного интерфейса является метод `write()` файлового объекта. Любой объект, способный экспортировать последовательность байтов через буферный интерфейс, может быть записан в файл. Существует ряд кодов формата для [`PyArg_ParseTuple()`](https://python-all.ru/2.7/c-api/arg.html#c.PyArg_ParseTuple), которые работают с буферным интерфейсом объекта, возвращая данные из целевого объекта.

Начиная с версии 1.6, Python предоставляет буферные объекты на уровне Python и буферный API на уровне C, чтобы любой встроенный или определённый пользователем тип мог предоставлять свои характеристики. Однако оба были объявлены устаревшими из-за различных недостатков и официально удалены в Python 3 в пользу нового буферного API на уровне C и нового объекта на уровне Python с именем [`memoryview`](https://python-all.ru/2.7/library/stdtypes.html#memoryview).

Новый буферный API был портирован в Python 2.6, а объект [`memoryview`](https://python-all.ru/2.7/library/stdtypes.html#memoryview) – в Python 2.7. Настоятельно рекомендуется использовать их вместо старых API, если только это невозможно по соображениям совместимости.

## Структура Py\_buffer нового стиля

**`Py_buffer`**

**void \*`buf`**

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

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

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

**int `readonly`**

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

**const char \*`format`**

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

Если все под-смещения отрицательны (т.е. разыменование не требуется), то это поле должно быть NULL (значение по умолчанию).

Ниже приведена функция, возвращающая указатель на элемент в 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/2.7/c-api/buffer.html#c.PyBuffer_SizeFromFormat), однако экспортёр может знать эту информацию без разбора строки формата, и для правильной интерпретации шагового доступа необходимо знать размер элемента. Поэтому его хранение удобнее и быстрее.

**void \*`internal`**

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

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

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

Возвращает `1`, если *obj* поддерживает буферный интерфейс, в противном случае `0`.

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

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

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

`0` возвращается в случае успеха и `-1` в случае ошибки.

В следующей таблице приведены возможные значения аргумента *flags*.

| Флаг | Описание |
| --- | --- |
| `PyBUF_SIMPLE` | Это состояние флага по умолчанию. Возвращаемый буфер может иметь или не иметь память для записи. Формат данных будет считаться беззнаковыми байтами. Это «автономная» константа флага. Её никогда не нужно комбинировать с другими с помощью оператора '\|'. Экспортёр вызовет ошибку, если не сможет предоставить такой непрерывный буфер байтов. |
| `PyBUF_WRITABLE` | Возвращаемый буфер должен быть доступен для записи. Если это не так, вызывается ошибка. |
| `PyBUF_STRIDES` | Это подразумевает `PyBUF_ND`. Возвращаемый буфер должен предоставлять информацию о шагах (т.е. шаги не могут быть NULL). Это используется, когда потребитель может работать с шаговыми, несмежными массивами. Обработка шагов автоматически подразумевает, что можно работать с формой. Экспортёр может вызвать ошибку, если шаговое представление данных невозможно (т.е. без suboffsets). |
| `PyBUF_ND` | Возвращаемый буфер должен предоставлять информацию о форме. Память считается C-стиль непрерывной (последнее измерение меняется быстрее всего). Экспортёр может вызвать ошибку, если не может предоставить такой непрерывный буфер. Если этот флаг не задан, форма будет *NULL*. |
| `PyBUF_C_CONTIGUOUS` `PyBUF_F_CONTIGUOUS` `PyBUF_ANY_CONTIGUOUS` | Эти флаги указывают, что возвращаемый буфер должен быть соответственно C-непрерывным (последнее измерение изменяется быстрее всего), непрерывным по Фортрану (первое измерение изменяется быстрее всего) или любым из них. Все эти флаги подразумевают `PyBUF_STRIDES` и гарантируют, что структура информации о шагах будет заполнена корректно. |
| `PyBUF_INDIRECT` | Этот флаг указывает, что возвращаемый буфер должен содержать информацию о suboffsets (которая может быть NULL, если suboffsets не нужны). Это может использоваться, когда потребитель может работать с косвенной ссылкой на массив, подразумеваемой этими suboffsets. Это подразумевает `PyBUF_STRIDES`. |
| `PyBUF_FORMAT` | Возвращаемый буфер должен содержать информацию о формате, если этот флаг указан. Это используется, когда потребитель будет проверять, какой «тип» данных на самом деле хранится. Экспортер всегда должен предоставлять эту информацию по запросу. Если формат не был явно запрошен, он должен быть возвращён как *NULL* (что означает `'B'`, или unsigned bytes). |
| `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/2.7/c-api/buffer.html#c.Py_buffer.itemsize) из структуры `format` в стиле struct.

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

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

#### `void PyBuffer_FillContiguousStrides(int ndims, Py_ssize_t *shape, Py_ssize_t *strides, int 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` (с возбуждением ошибки) в случае ошибки.

## Объекты MemoryView

Новое в версии 2.7.

Объект [`memoryview`](https://python-all.ru/2.7/library/stdtypes.html#memoryview) предоставляет новый интерфейс буфера на уровне C в виде объекта Python, который затем можно передавать, как любой другой объект.

#### `PyObject *PyMemoryView_FromObject(PyObject *obj)`

Создаёт объект memoryview из объекта, определяющего новый интерфейс буфера.

#### `PyObject *PyMemoryView_FromBuffer(Py_buffer *view)`

Создаёт объект memoryview, оборачивающий заданную структуру информации о буфере *view*. Объект memoryview затем становится владельцем буфера, то есть не следует пытаться освобождать его самостоятельно: он будет освобождён при удалении объекта memoryview.

#### `PyObject *PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order)`

Создаёт объект memoryview для непрерывного блока памяти (в порядке «C» или «Фортран» *order*) на основе объекта, определяющего буферный интерфейс. Если память непрерывна, объект memoryview указывает на исходную память. В противном случае создаётся копия, и memoryview указывает на новый объект bytes.

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

Возвращает true, если объект *obj* является объектом memoryview. В настоящее время создание подклассов [`memoryview`](https://python-all.ru/2.7/library/stdtypes.html#memoryview) не разрешено.

#### `Py_buffer *PyMemoryView_GET_BUFFER(PyObject *obj)`

Возвращает указатель на структуру информации о буфере, обёрнутую заданным объектом. Объект **должен** быть экземпляром memoryview; этот макрос не проверяет его тип, это необходимо делать самостоятельно, иначе возможны сбои.

## Объекты буфера старого стиля

Дополнительная информация о старом интерфейсе буфера приведена в разделе [Структуры объектов буфера](https://python-all.ru/2.7/c-api/typeobj.html#buffer-structs), в описании [`PyBufferProcs`](https://python-all.ru/2.7/c-api/typeobj.html#c.PyBufferProcs).

«Объект буфера» определяется в заголовочном файле `bufferobject.h` (включаемом через `Python.h`). Эти объекты очень похожи на строковые объекты на уровне программирования на Python: они поддерживают срезы, индексацию, конкатенацию и некоторые другие стандартные строковые операции. Однако их данные могут поступать из одного из двух источников: из блока памяти или из другого объекта, который экспортирует интерфейс буфера.

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

**`PyBufferObject`**

Этот подтип [`PyObject`](https://python-all.ru/2.7/c-api/structures.html#c.PyObject) представляет объект буфера.

**[PyTypeObject](https://python-all.ru/2.7/c-api/type.html#c.PyTypeObject) `PyBuffer_Type`**

Экземпляр [`PyTypeObject`](https://python-all.ru/2.7/c-api/type.html#c.PyTypeObject), представляющий тип буфера Python; это тот же объект, что `buffer` и `types.BufferType` на уровне Python.

**int `Py_END_OF_BUFFER`**

Эта константа может передаваться в качестве параметра *size* в [`PyBuffer_FromObject()`](https://python-all.ru/2.7/c-api/buffer.html#c.PyBuffer_FromObject) или [`PyBuffer_FromReadWriteObject()`](https://python-all.ru/2.7/c-api/buffer.html#c.PyBuffer_FromReadWriteObject). Она указывает, что новый [`PyBufferObject`](https://python-all.ru/2.7/c-api/buffer.html#c.PyBufferObject) должен ссылаться на объект *base* от заданного *offset* до конца его экспортируемого буфера. Это позволяет вызывающему коду не запрашивать у объекта *base* его длину.

#### `int PyBuffer_Check(PyObject *p)`

Возвращает true, если аргумент имеет тип [`PyBuffer_Type`](https://python-all.ru/2.7/c-api/buffer.html#c.PyBuffer_Type).

#### `PyObject* PyBuffer_FromObject(PyObject *base, Py_ssize_t offset, Py_ssize_t size)`

*Возвращаемое значение: новая ссылка.*

Возвращает новый буферный объект только для чтения. Возбуждает [`TypeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.TypeError), если *base* не поддерживает протокол буфера только для чтения или не предоставляет ровно один сегмент буфера, или возбуждает [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError), если *offset* меньше нуля. Буфер будет хранить ссылку на объект *base*, и содержимое буфера будет ссылаться на интерфейс буфера объекта *base*, начиная с позиции *offset* и распространяясь на *size* байтов. Если *size* равно `Py_END_OF_BUFFER`, то содержимое нового буфера распространяется на всю длину экспортированных буферных данных объекта *base*.

Изменено в версии 2.5: Эта функция использовала тип `int` для *offset* и *size*. Это может потребовать изменений в вашем коде для корректной поддержки 64-битных систем.

#### `PyObject* PyBuffer_FromReadWriteObject(PyObject *base, Py_ssize_t offset, Py_ssize_t size)`

*Возвращаемое значение: новая ссылка.*

Возвращает новый буферный объект для записи. Параметры и исключения аналогичны таковым для [`PyBuffer_FromObject()`](https://python-all.ru/2.7/c-api/buffer.html#c.PyBuffer_FromObject). Если объект *base* не экспортирует протокол буфера для записи, то возбуждается [`TypeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.TypeError).

Изменено в версии 2.5: Эта функция использовала тип `int` для *offset* и *size*. Это может потребовать изменений в вашем коде для корректной поддержки 64-битных систем.

#### `PyObject* PyBuffer_FromMemory(void *ptr, Py_ssize_t size)`

*Возвращаемое значение: новая ссылка.*

Возвращает новый буферный объект только для чтения, который читает из указанного места в памяти с указанным размером. Вызывающий отвечает за то, чтобы буфер памяти, переданный как *ptr*, не был освобождён, пока существует возвращаемый буферный объект. Вызывает [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError), если *size* меньше нуля. Обратите внимание, что `Py_END_OF_BUFFER` может *не* быть передан для параметра *size*; в этом случае будет вызвано [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError).

Изменено в версии 2.5: Эта функция использовала тип `int` для *size*. Это может потребовать изменений в коде для корректной поддержки 64-битных систем.

#### `PyObject* PyBuffer_FromReadWriteMemory(void *ptr, Py_ssize_t size)`

*Возвращаемое значение: новая ссылка.*

Аналог [`PyBuffer_FromMemory()`](https://python-all.ru/2.7/c-api/buffer.html#c.PyBuffer_FromMemory), но возвращаемый буфер доступен для записи.

Изменено в версии 2.5: Эта функция использовала тип `int` для *size*. Это может потребовать изменений в коде для корректной поддержки 64-битных систем.

#### `PyObject* PyBuffer_New(Py_ssize_t size)`

*Возвращаемое значение: новая ссылка.*

Возвращает новый буферный объект для записи, который управляет собственным буфером памяти размером *size* байт. [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError) возвращается, если *size* не является нулём или положительным числом. Обратите внимание, что буфер памяти (возвращаемый [`PyObject_AsWriteBuffer()`](https://python-all.ru/2.7/c-api/objbuffer.html#c.PyObject_AsWriteBuffer)) не имеет специального выравнивания.

Изменено в версии 2.5: Эта функция использовала тип `int` для *size*. Это может потребовать изменений в коде для корректной поддержки 64-битных систем.
