Протокол буфера¶
Некоторые объекты, доступные в Python, имеют доступ к базовому массиву
памяти или буферу. К таким объектам относятся встроенные bytes
и
bytearray
, а также некоторые типы расширений, такие как array.array
. Сторонние
библиотеки могут определять свои собственные типы для специальных целей, таких
как обработка изображений или числовой анализ.
Хотя каждый из этих типов имеет свою собственную семантику, они имеют общую характеристику, обеспечиваемую возможно большим буфером памяти. Затем в некоторых ситуациях желательно получить доступ к этому буферу непосредственно и без промежуточного копирования.
Python обеспечивает такое средство на уровне C в виде буферного протокола. Этот протокол содержит две стороны:
- на стороне производителя тип может экспортировать «интерфейс буфера», который позволяет объектам этого типа предоставлять информацию об их базовом буфере. Этот интерфейс описан в разделе Буферные структуры объектов;
- на стороне потребителя доступно несколько средств для получения указателя на исходные базовые данные объекта (например, параметр метода).
Простые объекты, такие как bytes
и bytearray
, предоставляют свой базовый
буфер в байтоориентированной форме. Возможны и другие формы; например, элементы,
предоставляемые array.array
, могут быть многобайтовыми значениями.
Примером потребителя буферного интерфейса является write()
метод файловых
объектов: любой объект, который может экспортировать серию байтов через буферный
интерфейс, может быть записан в файл. Хотя write()
требует доступа
только для чтения к внутреннему содержимому переданного ему объекта, другие
методы, такие как readinto()
, нуждаются в доступе для записи к содержимому их
аргумента. Интерфейс буфера позволяет объектам выборочно разрешать или отклонять
экспорт буферов только для чтения и записи.
Для потребителя интерфейса буфера существует два способа получения буфера над целевым объектом:
- вызов
PyObject_GetBuffer()
с правильными параметрами; - вызов
PyArg_ParseTuple()
(или одного из его братьев и сестер) с одним изy*
,w*
илиs*
коды формата.
В обоих случаях необходимо вызывать PyBuffer_Release()
, когда буфер больше не нужен.
Невыполнение этого требования может привести к возникновению различных проблем,
таких как утечка ресурсов.
Буферная структура¶
Буферные структуры (или просто «буферы») полезны как способ предоставления двоичных данных от другого объекта Python программисту. Их также можно использовать как механизм слайсинга с нулевой копией. Используя их способность ссылаться на блок памяти, можно довольно легко выставить Python программисту любые данные. Память может быть большим, постоянным массивом в расширении C, это может быть необработанный блок памяти для манипуляции перед передачей в библиотеку операционной системы, или это может быть используемый для передачи структурированных данных в собственном формате в памяти.
В отличие от большинства типов данных, отображаемых Python интерпретатором,
буферы не являются указателями PyObject
, а простыми структурами C. Это
позволяет создавать и копировать их очень просто. Если необходима универсальная
обертка вокруг буфера, можно создать объект memoryview.
Краткие инструкции по написанию экспортируемого объекта см. в разделе
Структуры буферных объектов. Сведения о получении буфера см. в разделе PyObject_GetBuffer()
.
-
Py_buffer
¶ -
void *
buf
¶ Указатель на начало логической структуры, описываемой буферными полями. Это может быть любое местоположение в базовом блоке физической памяти экспортера. Например, при отрицательных
strides
значение может указывать на конец блока памяти.Для смежных массивов значение указывает на начало блока памяти.
-
void *
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()
обратилось к не-NULL
format
значению.Важное исключение: если потребитель запрашивает буфер без флага
PyBUF_FORMAT
,format
будет установлен вNULL
, но уitemsize
все еще есть значение для исходного формата.Если
shape
присутствует,product(shape) * itemsize == len
равенства по-прежнему сохраняется, и потребитель может использоватьitemsize
для навигации по буферу.Если
shape
NULL
в результатеPyBUF_SIMPLE
илиPyBUF_WRITABLE
запроса, потребитель должен игнорироватьitemsize
и принятьitemsize == 1
.
-
const char *
format
¶ Завершаемая NUL строка в синтаксисе стиля модуля
struct
описывающем содержимое одного элемента. Если этоNULL
, предполагается"B"
(беззнаковые байты).Это поле управляется флагом
PyBUF_FORMAT
.
-
int
ndim
¶ Количество измерений, представленных памятью в виде n-мерного массива. Если оно
0
,buf
указывает на один элемент, представляющий скаляр. В этом случаеshape
,strides
иsuboffsets
ДОЛЖНЫ бытьNULL
.Макрос
PyBUF_MAX_NDIM
ограничивает максимальное число размеров до 64. Экспортеры ДОЛЖНЫ соблюдать этот предел, потребители многомерных буферов ДОЛЖНЫ иметь возможность обрабатывать доPyBUF_MAX_NDIM
размеров.
-
Py_ssize_t *
shape
¶ Массив
Py_ssize_t
длиныndim
указывающий форму памяти как n-мерный массив. Обратите внимание, чтоshape[0] H1583404716 shape[ndim-1] * itemsize
ДОЛЖНО быть равноlen
.Значения формы ограничены
shape[n] >= 0
. Делоshape[n] == 0
требует особого внимания. Дополнительную информацию см. в разделе сложные массивы.Массив фигур доступен только для чтения потребителю.
-
Py_ssize_t *
strides
¶ Массив
Py_ssize_t
длиныndim
указывающий количество байтов, пропускаемых для перехода к новому элементу в каждом измерении.Stride значения может быть любым целым числом. Для обычных массивов шаги обычно бывают положительными, но потребитель ДОЛЖЕН уметь обрабатывать кейс
strides[n] <= 0
. Дополнительную информацию см. в разделе сложные массивы.Массив stades доступен только для чтения потребителю.
-
Py_ssize_t *
suboffsets
¶ Массив
Py_ssize_t
длинойndim
. Еслиsuboffsets[n] >= 0
, значения, хранящиеся вдоль n-го измерения, являются указателями, а значение субсмещений определяет количество байтов, добавляемых к каждому указателю после отмены ссылки. Отрицательное значение субсчета указывает на то, что отмена ссылок не должна происходить (чередование в смежном блоке памяти).Если все субсмещения отрицательны (то есть отмена ссылок не требуется), то это поле должно быть
NULL
(значение по умолчанию).Этот тип представления множества - используемый библиотекой отображения Python (PIL). Дополнительные сведения о доступе к элементам такого массива см. в разделе сложные массивы.
Массив субсмещений доступен только для чтения потребителю.
-
void *
internal
¶ Он предназначен для внутреннего использования экспортирующим объектом. Например, он может быть повторно приведен экспортером в виде целого числа и используемый для хранения флагов о том, должны ли быть освобождены массивы формы, шагов и субсмещений при освобождении буфера. Потребитель НЕ ДОЛЖЕН изменять это значение.
-
void *
Типы запросов буфера¶
Буферы обычно получаются путем отправки запроса буфера экспортирующему объекту
через PyObject_GetBuffer()
. Поскольку сложность логической структуры памяти может сильно
изменяться, потребитель использует аргумент flags для указания точного
типа буфера, который он может обрабатывать.
Все Py_buffer
поля однозначно определяются типом запроса.
независимые от запроса поля¶
Следующие поля не зависят от flags и всегда должны быть заполнены
правильным значениями: obj
, buf
, len
, itemsize
,
ndim
.
формат только для чтения¶
PyBUF_WRITABLE
может быть |“d к любому из флагов в следующем разделе. Поскольку
PyBUF_SIMPLE
определяется как 0, PyBUF_WRITABLE
может использоваться как автономный
флаг для запроса простого буфера для записи.
PyBUF_FORMAT
могут быть |“d к любому из флагов, кроме PyBUF_SIMPLE
. Последнее уже
подразумевает формат B
(беззнаковые байты).
форма, шаги, субсмещение¶
Флаги, управляющие логической структурой памяти, перечислены в порядке убывания сложности. Обратите внимание, что каждый флаг содержит все биты флагов под ним.
Запрос | форма | шаги | субсмещения |
---|---|---|---|
|
да | да | если нужно |
|
да | да | NULL |
|
да | NULL | NULL |
|
NULL | NULL | NULL |
запросы на непрерывность¶
В C или Fortran смежность может быть явно запрошена, с информацией о шаге и без нее. Без информации о шаге буфер должен быть C-смежным.
Запрос | форма | шаги | субсмещения | континг |
---|---|---|---|---|
|
да | да | NULL | C |
|
да | да | NULL | F |
|
да | да | NULL | C или F |
c:macro:: PyBUF_ND | да | NULL | NULL | C |
составные запросы¶
Все возможные запросы полностью определяются некоторым сочетанием флагов в предыдущем разделе. Для удобства буферный протокол предоставляет часто используемую комбинацию одиночных флагов.
В следующей таблице U обозначает неопределенную непрерывность.
Потребителю придется вызвать PyBuffer_IsContiguous()
, чтобы определить непрерывность.
Запрос | форма | шаги | субсмещения | континг | только для чтения | формат |
---|---|---|---|---|---|---|
|
да | да | если нужно | U | 0 | да |
|
да | да | если нужно | U | 1 или 0 | да |
|
да | да | NULL | U | 0 | да |
|
да | да | NULL | U | 1 или 0 | да |
|
да | да | NULL | U | 0 | NULL |
|
да | да | NULL | U | 1 или 0 | NULL |
|
да | NULL | NULL | C | 0 | NULL |
|
да | 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):
"""Убедитесь, что параметры представляют допустимый
массив в пределах аллоцированный памяти:
char *mem: начало блока физической памяти
memlen: длина блока физической памяти
смещения: (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-D массиве, на который
указывает 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)¶ Возвращает
1
, если obj поддерживает буферный интерфейс, в противном случае0
. Когда возвращается1
, это не гарантирует, чтоPyObject_GetBuffer()
добьется успеха. Эта функция всегда выполняется успешно.
-
int
PyObject_GetBuffer
(PyObject *exporter, Py_buffer *view, int flags)¶ Отправить запрос exporter на заполнение view в соответствии с указаниями flags. Если экспортер не может предоставить буфер точного типа, он ДОЛЖЕН поднять
PyExc_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)¶ Освободить буферный view и уменьшить ссылочный счетчик для
view->obj
. Эта функция ДОЛЖНА вызываться, когда буфер больше не используется, в противном случае могут возникнуть утечки ссылок.Ошибка при вызове этой функции в буфере, который не был получен с помощью
PyObject_GetBuffer()
.
-
Py_ssize_t
PyBuffer_SizeFromFormat
(const char *)¶ Возвращает подразумеваемое
itemsize
изformat
. Эта функция еще не реализована.
-
int
PyBuffer_IsContiguous
(Py_buffer *view, char order)¶ Возвращает
1
если память, определенная view, является C-стилем (order'C'
) или Fortran-стилем (order'F'
) смежный или одним из них (order'A'
). Возвращает0
иначе. Эта функция всегда выполняется успешно.
-
void*
PyBuffer_GetPointer
(Py_buffer *view, Py_ssize_t *indices)¶ Получить область памяти, на которую указывает indices внутри данного view. indices должен указывать на массив индексов
view->ndim
.
-
int
PyBuffer_FromContiguous
(Py_buffer *view, void *buf, Py_ssize_t len, char fort)¶ Копирование смежных байтов len из buf в view. fort могут быть
'C'
или'F'
(для упорядочения в стиле C или Fortran).0
возвращается при успехе,-1
при ошибке.
-
int
PyBuffer_ToContiguous
(void *buf, Py_buffer *src, Py_ssize_t len, char order)¶ Скопировать len байтов из src в его непрерывное представление в buf. order может быть
'C'
или'F'
или'A'
(для упорядочения в стиле C или Fortran или одного из них).0
возвращается при успехе,-1
при ошибке.Эта функция завершается неуспешно, если len != src->len.
-
void
PyBuffer_FillContiguousStrides
(int ndims, Py_ssize_t *shape, Py_ssize_t *strides, int itemsize, char order)¶ Заполнить массив 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)¶ Обработка запросов буфера для экспортера, который хочет предоставить buf размера len с возможностью записи, установленной в соответствии с только для чтения. buf интерпретируется как последовательность байтов без знака.
Аргумент flags указывает тип запроса. Эта функция всегда заполняет view, как указано флагами, если buf не обозначен как доступный только для чтения, а
PyBUF_WRITABLE
установлен в flags.При успехе устанавливается
view->obj
новой ссылке на exporter и возвращает 0. В противном случае поднимаетсяPyExc_BufferError
, устанавливаетсяview->obj
наNULL
и возвращает-1
;Если эта функция используется как часть getbufferproc, exporter ДОЛЖЕН быть установлен в объект экспорта, и flags должен быть передан неизмененным. В противном случае exporter ДОЛЖЕН быть
NULL
.