Spec-Zone.ru › Python 3.13

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

Некоторые объекты 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 (PIL). Дополнительные сведения о доступе к элементам такого массива см. в разделе сложные массивы.

Потребитель не может изменять массив смещений.

void *internal

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

Константы:

PyBUF_MAX_NDIM

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

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

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

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

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

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

Только для чтения, формат

PyBUF_WRITABLE

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

PyBUF_FORMAT

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

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

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

Форма, шаги, смещения

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

Запрос

форма

шаги

смещения

PyBUF_INDIRECT

да

да

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

PyBUF_STRIDES

да

да

NULL

PyBUF_ND

да

NULL

NULL

PyBUF_SIMPLE

NULL

NULL

NULL

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

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

Запрос

форма

шаги

смещения

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

PyBUF_C_CONTIGUOUS

да

да

NULL

C

PyBUF_F_CONTIGUOUS

да

да

NULL

F

PyBUF_ANY_CONTIGUOUS

да

да

NULL

C или F

PyBUF_ND

да

NULL

NULL

C

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

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

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

Запрос

форма

шаги

смещения

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

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

формат

PyBUF_FULL

да

да

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

U

0

да

PyBUF_FULL_RO

да

да

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

U

1 или 0

да

PyBUF_RECORDS

да

да

NULL

U

0

да

PyBUF_RECORDS_RO

да

да

NULL

U

1 или 0

да

PyBUF_STRIDED

да

да

NULL

U

0

NULL

PyBUF_STRIDED_RO

да

да

NULL

U

1 или 0

NULL

PyBUF_CONTIG

да

NULL

NULL

C

0

NULL

PyBUF_CONTIG_RO

да

NULL

NULL

C

1 или 0

NULL

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

В стиле NumPy: форма и шаги

Логическая структура массивов в стиле 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: форма, шаги и смещения

Помимо обычных элементов, массивы в стиле 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. Если экспортёр не может предоставить буфер точно заданного типа, он ДОЛЖЕН вызвать исключение 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 в соответствии с флагами, если только buf не помечен как доступный только для чтения и в flags не установлен PyBUF_WRITABLE.

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

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

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

Spec-Zone.ru

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