Spec-Zone.ru › Python 3.14

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

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

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

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

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

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

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

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

  • вызвать PyObject_GetBuffer() с нужными параметрами;
  • вызвать PyArg_ParseTuple() (или один из аналогичных вариантов) с одним из y*, w* или s* кодов формата.

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

Добавлено в версии 3.12: Теперь протокол буфера доступен в Python; см. разделы Эмуляция типов буфера и memoryview.

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

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

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

Краткие инструкции по написанию объекта-экспортёра см. в разделе Структуры объектов буфера. О получении буфера см. в описании PyObject_GetBuffer().

type Py_buffer
Входит в стабильный ABI (включая все поля) начиная с версии 3.11.
void *buf

Указатель на начало логической структуры, описанной полями буфера. Он может указывать на любое место в базовом физическом блоке памяти экспортёра. Например, при отрицательном значении strides указатель может указывать на конец блока памяти.

Для непрерывных массивов указатель указывает на начало блока памяти.

PyObject *obj

Новая ссылка на объект-экспортёр. Ссылка принадлежит потребителю и автоматически освобождается (то есть счётчик ссылок уменьшается), а её значение устанавливается в NULL функцией PyBuffer_Release(). Это поле эквивалентно возвращаемому значению любой стандартной функции C API.

В особом случае, когда временные буферы оборачиваются функциями PyMemoryView_FromBuffer() или PyBuffer_FillInfo(), этому полю присваивается NULL. В общем случае экспортирующие объекты НЕ ДОЛЖНЫ использовать этот способ.

Py_ssize_t len

product(shape) * itemsize. Для непрерывных массивов это длина базового блока памяти. Для непрерывных массивов это длина, которую имела бы логическая структура, если бы её скопировали в непрерывное представление.

Обращаться к ((char *)buf)[0] up to ((char *)buf)[len-1] допустимо, только если буфер получен по запросу, гарантирующему непрерывность. В большинстве случаев таким запросом будет PyBUF_SIMPLE или PyBUF_WRITABLE.

int readonly

Признак того, доступен ли буфер только для чтения. Значение этого поля определяется флагом PyBUF_WRITABLE.

Py_ssize_t itemsize

Размер одного элемента в байтах. Совпадает со значением struct.calcsize(), вызванной для значений format, не являющихся NULL.

Важное исключение: если потребитель запрашивает буфер без флага PyBUF_FORMAT, значение format будет установлено в NULL, однако itemsize по-прежнему будет содержать значение для исходного формата.

Если задано поле shape, равенство product(shape) * itemsize == len по-прежнему выполняется, и потребитель может использовать itemsize для перемещения по буферу.

Если поле shape равно NULL в результате запроса PyBUF_SIMPLE или PyBUF_WRITABLE, потребитель должен игнорировать itemsize и считать, что itemsize == 1.

char *format

Строка с завершающим символом NULL в синтаксисе модуля struct, описывающая содержимое одного элемента. Если это поле равно NULL, предполагается "B" (беззнаковые байты).

Значение этого поля определяется флагом PyBUF_FORMAT.

int ndim

Количество измерений памяти, представленной в виде n-мерного массива. Если значение равно 0, buf указывает на один элемент, представляющий скаляр. В этом случае shape, strides и suboffsets ДОЛЖНЫ быть равны NULL. Максимальное число измерений задаётся значением PyBUF_MAX_NDIM.

Py_ssize_t *shape

Массив значений Py_ssize_t длиной ndim, задающий форму памяти как n-мерного массива. Обратите внимание, что shape[0] * ... * shape[ndim-1] * itemsize ДОЛЖНО быть равно len.

Значения формы ограничены значением shape[n] >= 0. Случай shape[n] == 0 требует особого внимания. Дополнительные сведения см. в разделе сложные массивы.

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

Py_ssize_t *strides

Массив значений Py_ssize_t длиной ndim, задающий количество байтов, которые нужно пропустить для перехода к новому элементу в каждом измерении.

Значения шага могут быть любыми целыми числами. Для обычных массивов шаги, как правило, положительны, однако потребитель ОБЯЗАН обрабатывать и случай strides[n] <= 0. Дополнительные сведения см. в разделе сложные массивы.

Для потребителя массив шагов доступен только для чтения.

Py_ssize_t *suboffsets

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

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

Такое представление массива используется библиотекой Python Imaging Library (PIL). Дополнительные сведения о доступе к элементам такого массива см. в разделе сложные массивы.

Для потребителя массив смещений подмассивов доступен только для чтения.

void *internal

Это поле предназначено для внутреннего использования объектом-экспортёром. Например, экспортёр может привести его к типу integer и использовать для хранения флагов, указывающих, нужно ли освобождать массивы формы, шагов и смещений подмассивов при освобождении буфера. Потребитель НЕ ДОЛЖЕН изменять это значение.

Константы:

PyBUF_MAX_NDIM
Входит в стабильный ABI начиная с версии 3.11.

Максимальное число измерений, которое может иметь представление памяти. Экспортёры ДОЛЖНЫ соблюдать это ограничение; потребителям многомерных буферов СЛЕДУЕТ поддерживать до PyBUF_MAX_NDIM измерений. В настоящее время значение равно 64.

Типы запросов буфера

Обычно буферы получают, отправляя запрос буфера объекту-экспортёру с помощью PyObject_GetBuffer(). Поскольку сложность логической структуры памяти может сильно различаться, потребитель использует аргумент флаги, чтобы задать точный тип буфера, с которым он умеет работать.

Все поля Py_buffer однозначно определяются типом запроса.

Поля, не зависящие от запроса

На следующие поля не влияют флаги, поэтому они всегда должны заполняться правильными значениями: obj, buf, len, itemsize, ndim.

readonly, format

PyBUF_WRITABLE
Входит в стабильный ABI начиная с версии 3.11.

Управляет полем readonly. Если флаг установлен, экспортёр ОБЯЗАН предоставить доступный для записи буфер или сообщить об ошибке. В противном случае экспортёр МОЖЕТ предоставить буфер только для чтения или для чтения и записи, но выбранный вариант ДОЛЖЕН быть одинаковым для всех потребителей. Например, выражение PyBUF_SIMPLE | PyBUF_WRITABLE можно использовать для запроса простого буфера, доступного для записи.

PyBUF_WRITEABLE

Это псевдоним для PyBUF_WRITABLE.

Мягко устарел начиная с версии 3.13.

PyBUF_FORMAT
Входит в стабильный ABI начиная с версии 3.11.

Управляет полем format. Если флаг установлен, это поле ДОЛЖНО быть заполнено правильно. В противном случае это поле ДОЛЖНО быть равно NULL.

К флагу PyBUF_WRITABLE можно применить операцию | с любым флагом из следующего раздела. Поскольку PyBUF_SIMPLE определён как 0, PyBUF_WRITABLE можно использовать как самостоятельный флаг для запроса простого буфера, доступного для записи.

К флагу PyBUF_FORMAT необходимо применить операцию | с любым флагом, кроме PyBUF_SIMPLE, поскольку последний уже подразумевает формат B (беззнаковые байты). Флаг PyBUF_FORMAT нельзя использовать самостоятельно.

shape, strides, suboffsets

Флаги, управляющие логической структурой памяти, перечислены в порядке убывания сложности. Обратите внимание: каждый флаг включает все биты флагов, перечисленных ниже.

Запрос

shape

strides

suboffsets

PyBUF_INDIRECT
Входит в стабильный ABI начиная с версии 3.11.

да

да

если требуется

PyBUF_STRIDES
Входит в стабильный ABI начиная с версии 3.11.

да

да

NULL

PyBUF_ND
Входит в стабильный ABI начиная с версии 3.11.

да

NULL

NULL

PyBUF_SIMPLE
Входит в стабильный ABI начиная с версии 3.11.

NULL

NULL

NULL

Запросы непрерывности

Можно явно запросить непрерывность в стиле C или Fortran, с информацией о шагах или без неё. Если информация о шагах не указана, буфер должен быть непрерывным в стиле C.

Запрос

shape

strides

suboffsets

непрерывность

PyBUF_C_CONTIGUOUS
Входит в стабильный ABI начиная с версии 3.11.

да

да

NULL

C

PyBUF_F_CONTIGUOUS
Входит в стабильный ABI начиная с версии 3.11.

да

да

NULL

F

PyBUF_ANY_CONTIGUOUS
Входит в стабильный ABI начиная с версии 3.11.

да

да

NULL

C или F

PyBUF_ND

да

NULL

NULL

C

Составные запросы

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

В следующей таблице U обозначает неопределённую непрерывность. Чтобы определить непрерывность, потребителю придётся вызвать PyBuffer_IsContiguous().

Запрос

shape

strides

suboffsets

непрерывность

только для чтения

формат

PyBUF_FULL
Входит в стабильный ABI начиная с версии 3.11.

да

да

если требуется

U

0

да

PyBUF_FULL_RO
Входит в стабильный ABI начиная с версии 3.11.

да

да

если требуется

U

1 или 0

да

PyBUF_RECORDS
Входит в стабильный ABI начиная с версии 3.11.

да

да

NULL

U

0

да

PyBUF_RECORDS_RO
Входит в стабильный ABI начиная с версии 3.11.

да

да

NULL

U

1 или 0

да

PyBUF_STRIDED
Входит в стабильный ABI начиная с версии 3.11.

да

да

NULL

U

0

NULL

PyBUF_STRIDED_RO
Входит в стабильный ABI начиная с версии 3.11.

да

да

NULL

U

1 или 0

NULL

PyBUF_CONTIG
Входит в стабильный ABI начиная с версии 3.11.

да

NULL

NULL

C

0

NULL

PyBUF_CONTIG_RO
Входит в стабильный ABI начиная с версии 3.11.

да

NULL

NULL

C

1 или 0

NULL

Сложные массивы

В стиле NumPy: shape и strides

Логическая структура массивов в стиле NumPy определяется полями itemsize, ndim, shape и strides.

Если ndim == 0, ячейка памяти, на которую указывает buf, интерпретируется как скаляр размером itemsize. В этом случае и shape, и strides равны NULL.

Если strides равно NULL, массив интерпретируется как стандартный n-мерный массив в стиле C. В противном случае потребитель должен обращаться к n-мерному массиву следующим образом:

ptr = (char *)buf + indices[0] * strides[0] + ... + indices[n-1] * strides[n-1];
item = *((typeof(item) *)ptr);

Как отмечалось выше, buf может указывать на любое место в фактическом блоке памяти. Экспортёр может проверить корректность буфера с помощью этой функции:

def verify_structure(memlen, itemsize, ndim, shape, strides, offset):
    """Verify that the parameters represent a valid array within
       the bounds of the allocated memory:
           char *mem: start of the physical memory block
           memlen: length of the physical memory block
           offset: (char *)buf - mem
    """
    if offset % itemsize:
        return False
    if offset < 0 or offset+itemsize > memlen:
        return False
    if any(v % itemsize for v in strides):
        return False

    if ndim <= 0:
        return ndim == 0 and not shape and not strides
    if 0 in shape:
        return True

    imin = sum(strides[j]*(shape[j]-1) for j in range(ndim)
               if strides[j] <= 0)
    imax = sum(strides[j]*(shape[j]-1) for j in range(ndim)
               if strides[j] > 0)

    return 0 <= offset+imin and offset+imax+itemsize <= memlen

В стиле PIL: shape, strides и suboffsets

Помимо обычных элементов, массивы в стиле PIL могут содержать указатели, по которым необходимо перейти, чтобы получить следующий элемент измерения. Например, обычный трёхмерный массив в стиле C char v[2][2][3] можно также представить как массив из 2 указателей на 2 двумерных массива: char (*v[2])[2][3]. В представлении со смещениями подмассивов эти два указателя можно поместить в начало buf; они будут указывать на два массива char x[2][3], которые могут находиться в любом месте памяти.

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

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;
}

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

int PyObject_CheckBuffer(PyObject *obj)
Входит в Стабильный ABI начиная с версии 3.11.

Возвращает 1, если obj поддерживает интерфейс буфера, и 0 в противном случае. Возврат 1 не гарантирует успешное выполнение PyObject_GetBuffer(). Эта функция всегда завершается успешно.

int PyObject_GetBuffer(PyObject *exporter, Py_buffer *view, int flags)
Входит в Стабильный ABI начиная с версии 3.11.

Отправляет запрос exporter на заполнение view в соответствии с flags. Если exporter не может предоставить буфер требуемого типа, он ДОЛЖЕН вызвать исключение BufferError, установить view->obj в NULL и вернуть -1.

В случае успеха заполняет view, устанавливает view->obj в новую ссылку на exporter и возвращает 0. В случае цепочки поставщиков буферов, перенаправляющих запросы к одному объекту, view->obj МОЖЕТ ссылаться на этот объект вместо exporter (см. Структуры объектов буфера).

Успешные вызовы PyObject_GetBuffer() должны сопровождаться вызовами PyBuffer_Release(), аналогично malloc() и free(). Таким образом, после того как потребитель закончит работу с буфером, PyBuffer_Release() необходимо вызвать ровно один раз.

void PyBuffer_Release(Py_buffer *view)
Входит в Стабильный ABI начиная с версии 3.11.

Освобождает буфер view и снимает сильную ссылку (то есть уменьшает счётчик ссылок) на поддерживающий объект представления — view->obj. Эту функцию НЕОБХОДИМО вызвать, когда буфер больше не используется, иначе возможны утечки ссылок.

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

Py_ssize_t PyBuffer_SizeFromFormat(const char *format)
Входит в Стабильный ABI начиная с версии 3.11.

Возвращает подразумеваемое значение itemsize на основе format. В случае ошибки вызывает исключение и возвращает -1.

Добавлено в версии 3.9.

int PyBuffer_IsContiguous(const Py_buffer *view, char order)
Входит в Стабильный ABI начиная с версии 3.11.

Возвращает 1, если память, определяемая view, является непрерывной в стиле C (order равен 'C'), в стиле Fortran (order равен 'F') или в любом из этих порядков (order равен 'A'). В противном случае возвращает 0. Эта функция всегда завершается успешно.

void *PyBuffer_GetPointer(const Py_buffer *view, const Py_ssize_t *indices)
Входит в Стабильный ABI начиная с версии 3.11.

Возвращает область памяти, на которую указывают indices внутри заданного view. indices должен указывать на массив из view->ndim индексов.

int PyBuffer_FromContiguous(const Py_buffer *view, const void *buf, Py_ssize_t len, char fort)
Входит в Стабильный ABI начиная с версии 3.11.

Копирует len непрерывных байтов из buf в view. fort может иметь значение 'C' или 'F' (для порядка в стиле C или Fortran). В случае успеха возвращается 0, в случае ошибки — -1.

int PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char order)
Входит в Стабильный ABI начиная с версии 3.11.

Копирует len байтов из src в его непрерывное представление в buf. order может иметь значение 'C', 'F' или 'A' (для порядка в стиле C, Fortran или любого из них). В случае успеха возвращается 0, в случае ошибки — -1.

Эта функция завершается ошибкой, если len != src->len.

int PyObject_CopyData(PyObject *dest, PyObject *src)
Входит в Стабильный ABI начиная с версии 3.11.

Копирует данные из буфера src в буфер dest. Может преобразовывать буферы между порядком в стиле C и Fortran.

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

void PyBuffer_FillContiguousStrides(int ndims, Py_ssize_t *shape, Py_ssize_t *strides, int itemsize, char order)
Входит в Стабильный ABI начиная с версии 3.11.

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

int PyBuffer_FillInfo(Py_buffer *view, PyObject *exporter, void *buf, Py_ssize_t len, int readonly, int flags)
Входит в Стабильный ABI начиная с версии 3.11.

Обрабатывает запросы буфера для экспортера, который хочет предоставить buf размером len с возможностью записи, заданной параметром readonly. buf рассматривается как последовательность беззнаковых байтов.

Аргумент flags указывает тип запроса. Эта функция всегда заполняет view в соответствии с flags, за исключением случая, когда buf обозначен как доступный только для чтения и в flags установлен PyBUF_WRITABLE.

В случае успеха устанавливает view->obj в новую ссылку на exporter и возвращает 0. В противном случае вызывает исключение BufferError, устанавливает view->obj в NULL и возвращает -1;

Если эта функция используется как часть getbufferproc, exporter ДОЛЖЕН быть установлен в экспортирующий объект, а flags должны передаваться без изменений. В противном случае exporter ДОЛЖЕН иметь значение NULL.

© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/c-api/buffer.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API