Buffertprotokoll¶
Vissa objekt som finns i Python ger tillgång till en underliggande minnesarray eller buffer. Sådana objekt inkluderar de inbyggda bytes
och bytearray
, och vissa tilläggstyper som array.array
. Tredjepartsbibliotek kan definiera sina egna typer för speciella ändamål, t.ex. bildbehandling eller numerisk analys.
Även om var och en av dessa typer har sin egen semantik, har de den gemensamma egenskapen att de backas upp av en eventuellt stor minnesbuffert. I vissa situationer är det då önskvärt att komma åt den bufferten direkt och utan mellanliggande kopiering.
Python tillhandahåller en sådan möjlighet på C- och Python-nivå i form av buffer protocol. Detta protokoll har två sidor:
på producentsidan kan en typ exportera ett ”buffertgränssnitt” som tillåter objekt av den typen att exponera information om sin underliggande buffert. Detta gränssnitt beskrivs i avsnittet Strukturer för buffertobjekt; för Python se Emulering av bufferttyper.
på konsumentsidan finns det flera sätt att få en pekare till de underliggande rådata för ett objekt (t.ex. en metodparameter). För Python se
memoryview
.
Enkla objekt som bytes
och bytearray
exponerar sin underliggande buffert i byteorienterad form. Andra former är möjliga; till exempel kan elementen som exponeras av en array.array
vara multibyte-värden.
Ett exempel på en användare av buffertgränssnittet är metoden write()
för filobjekt: alla objekt som kan exportera en serie byte genom buffertgränssnittet kan skrivas till en fil. Medan write()
endast behöver skrivskyddad åtkomst till det interna innehållet i det objekt som skickas till den, behöver andra metoder som readinto()
skrivåtkomst till innehållet i sitt argument. Buffertgränssnittet gör det möjligt för objekt att selektivt tillåta eller avvisa export av skrivläsningsbuffertar och skrivskyddade buffertar.
Det finns två sätt för en användare av buffertgränssnittet att skaffa en buffert över ett målobjekt:
anropa
PyObject_GetBuffer()
med rätt parametrar;anropa
PyArg_ParseTuple()
(eller ett av dess syskon) med en avy*
,w*
ellers*
formatkoder.
I båda fallen måste PyBuffer_Release()
anropas när bufferten inte längre behövs. Om detta inte görs kan det leda till olika problem, t.ex. resursläckage.
Tillagd i version 3.12: Buffertprotokollet är nu tillgängligt i Python, se Emulering av bufferttyper och memoryview
.
Buffertstruktur¶
Buffertstrukturer (eller helt enkelt ”buffertar”) är användbara som ett sätt att exponera binärdata från ett annat objekt till Python-programmeraren. De kan också användas som en nollkopieringsmekanism. Genom att använda deras förmåga att referera till ett minnesblock är det möjligt att exponera vilken data som helst för Python-programmeraren ganska enkelt. Minnet kan vara en stor, konstant array i ett C-tillägg, det kan vara ett råminnesblock för manipulation innan det skickas till ett operativsystembibliotek, eller det kan användas för att skicka runt strukturerade data i sitt ursprungliga format i minnet.
Till skillnad från de flesta datatyper som exponeras av Python-tolken är buffertar inte PyObject
-pekare utan snarare enkla C-strukturer. Detta gör att de kan skapas och kopieras mycket enkelt. När ett generiskt omslag runt en buffert behövs kan ett memoryview-objekt skapas.
För korta instruktioner om hur man skriver ett exporterande objekt, se Buffer Object Structures. För att hämta en buffert, se PyObject_GetBuffer()
.
-
type Py_buffer¶
- En del av Stabil ABI (inklusive alla medlemmar) sedan version 3.11.
-
void *buf¶
En pekare till början av den logiska struktur som beskrivs av buffertfälten. Detta kan vara vilken plats som helst inom det underliggande fysiska minnesblocket hos exportören. Till exempel, med negativ
strides
kan värdet peka till slutet av minnesblocket.För contiguous arrays pekar värdet på början av minnesblocket.
-
PyObject *obj¶
En ny referens till det exporterande objektet. Referensen ägs av konsumenten och frigörs automatiskt (d.v.s. referensantalet minskas) och sätts till
NULL
avPyBuffer_Release()
. Fältet motsvarar returvärdet för vilken standard C-API-funktion som helst.Som ett specialfall, för temporära buffertar som är omslutna av
PyMemoryView_FromBuffer()
ellerPyBuffer_FillInfo()
är detta fältNULL
. Generellt gäller att exporterande objekt INTE får använda detta schema.
-
Py_ssize_t len¶
product(shape) * itemsize
. För sammanhängande arrayer är detta längden på det underliggande minnesblocket. För icke sammanhängande matriser är det den längd som den logiska strukturen skulle ha om den kopierades till en sammanhängande representation.Att komma åt
((char *)buf)[0] up to ((char *)buf)[len-1]
är bara giltigt om bufferten har erhållits genom en begäran som garanterar sammanhängande. I de flesta fall kommer en sådan begäran att varaPyBUF_SIMPLE
ellerPyBUF_WRITABLE
.
-
int readonly¶
En indikator på om bufferten är skrivskyddad. Detta fält styrs av
PyBUF_WRITABLE
flaggan.
-
Py_ssize_t itemsize¶
Elementstorlek i bytes för ett enda element. Samma som värdet av
struct.calcsize()
som anropas på icke-NULL
format
-värden.Viktigt undantag: Om en konsument begär en buffert utan
PyBUF_FORMAT
flaggan, kommerformat
att sättas tillNULL
, menitemsize
har fortfarande värdet för det ursprungliga formatet.Om
shape
är närvarande gäller fortfarande likhetenproduct(shape) * itemsize == len
och konsumenten kan användaitemsize
för att navigera i bufferten.Om
shape
ärNULL
som ett resultat av enPyBUF_SIMPLE
eller enPyBUF_WRITABLE
begäran, måste konsumenten bortse frånitemsize
och antaitemsize == 1
.
-
char *format¶
En NULL-terminerad sträng i
struct
-modulstilssyntax som beskriver innehållet i ett enda objekt. Om detta ärNULL
, antas"B"
(osignerade bytes).Detta fält styrs av flaggan
PyBUF_FORMAT
.
-
int ndim¶
Antalet dimensioner som minnet representerar som en n-dimensionell array. Om det är
0
pekarbuf
på ett enda objekt som representerar en skalär. I detta fall MÅSTEshape
,strides
ochsuboffsets
varaNULL
. Det maximala antalet dimensioner anges avPyBUF_MAX_NDIM
.
-
Py_ssize_t *shape¶
En array av
Py_ssize_t
med längdenndim
som anger formen på minnet som en n-dimensionell array. Observera attshape[0] * ... * shape[ndim-1] * itemsize
MÅSTE vara lika medlen
.Shape-värden är begränsade till
shape[n] >= 0
. Falletshape[n] == 0
kräver särskild uppmärksamhet. Se komplexa matriser för ytterligare information.Shape-arrayen är skrivskyddad för konsumenten.
-
Py_ssize_t *strides¶
En array av
Py_ssize_t
med längdenndim
som anger antalet bytes som ska hoppas över för att komma till ett nytt element i varje dimension.Stride-värden kan vara valfritt heltal. För vanliga arrayer är strides vanligtvis positiva, men en konsument MÅSTE kunna hantera fallet
strides[n] <= 0
. Se complex arrays för mer information.Strides-arrayen är skrivskyddad för konsumenten.
-
Py_ssize_t *suboffsets¶
En array av
Py_ssize_t
med längdenndim
. Omsuboffsets[n] >= 0
är värdena som lagras längs den n:te dimensionen pekare och suboffset-värdet anger hur många byte som ska läggas till varje pekare efter de-referering. Ett negativt suboffset-värde anger att ingen de-referensering ska ske (striding i ett sammanhängande minnesblock).Om alla suboffsets är negativa (dvs. ingen de-referensering behövs), måste detta fält vara
NULL
(standardvärdet).Denna typ av matrisrepresentation används av Python Imaging Library (PIL). Se komplexa arrayer för mer information om hur du får tillgång till element i en sådan array.
Suboffsets-arrayen är skrivskyddad för konsumenten.
-
void *internal¶
Detta är för intern användning av det exporterande objektet. Det kan t.ex. omformas till ett heltal av exportören och användas för att lagra flaggor om huruvida arrayer med shape, strides och suboffsets måste frigöras när bufferten släpps. Konsumenten MÅSTE INTE ändra detta värde.
-
void *buf¶
Konstanter:
-
PyBUF_MAX_NDIM¶
Det maximala antalet dimensioner som minnet representerar. Exportörer MÅSTE respektera denna gräns, konsumenter av flerdimensionella buffertar BÖR kunna hantera upp till
PyBUF_MAX_NDIM
dimensioner. För närvarande satt till 64.
Typer av buffertförfrågningar¶
Buffertar erhålls vanligtvis genom att skicka en buffertförfrågan till ett exporterande objekt via PyObject_GetBuffer()
. Eftersom komplexiteten i minnets logiska struktur kan variera drastiskt använder konsumenten argumentet flags för att ange den exakta bufferttyp som den kan hantera.
Alla fält i Py_buffer
definieras otvetydigt av typen av begäran.
fält oberoende av begäran¶
Följande fält påverkas inte av flags och måste alltid fyllas i med korrekta värden: obj
, buf
, len
, itemsize
, ndim
.
skrivskyddad, format¶
- PyBUF_WRITABLE¶
Kontrollerar fältet
readonly
. Om det är inställt MÅSTE exportören tillhandahålla en skrivbar buffert, annars rapporteras fel. I annat fall KAN exportören tillhandahålla antingen en skrivskyddad eller skrivbar buffert, men valet MÅSTE vara konsekvent för alla konsumenter. Till exempel kan PyBUF_SIMPLE | PyBUF_WRITABLE användas för att begära en enkel skrivbar buffert.
PyBUF_WRITABLE
kan kopplas till någon av flaggorna i nästa avsnitt. Eftersom PyBUF_SIMPLE
är definierad som 0, kan PyBUF_WRITABLE
användas som en fristående flagga för att begära en enkel skrivbar buffert.
PyBUF_FORMAT
måste vara |’d till någon av flaggorna utom PyBUF_SIMPLE
, eftersom den senare redan implicerar format B
(osignerade byte). PyBUF_FORMAT
kan inte användas på egen hand.
form, strides, suboffsets¶
Flaggorna som styr minnets logiska struktur är listade i fallande ordning efter komplexitet. Observera att varje flagga innehåller alla bitar i flaggorna under den.
Förfrågan |
form |
steg |
delmängder |
---|---|---|---|
|
ja |
ja |
om det behövs |
|
ja |
ja |
NULL |
|
ja |
NULL |
NULL |
|
NULL |
NULL |
NULL |
begäran om angränsning¶
C eller Fortran contiguity kan uttryckligen begäras, med eller utan stride-information. Utan stride-information måste bufferten vara C-kontigu.
Förfrågan |
form |
steg |
delmängder |
kontigent |
---|---|---|---|---|
|
ja |
ja |
NULL |
C |
|
ja |
ja |
NULL |
F |
|
ja |
ja |
NULL |
C eller F |
ja |
NULL |
NULL |
C |
sammansatta förfrågningar¶
Alla möjliga förfrågningar definieras fullt ut av någon kombination av flaggorna i föregående avsnitt. För enkelhetens skull tillhandahåller buffertprotokollet ofta använda kombinationer som enskilda flaggor.
I följande tabell står U för undefined contiguity (odefinierad sammanhängande). Konsumenten måste anropa PyBuffer_IsContiguous()
för att avgöra sammanhängande.
Förfrågan |
form |
steg |
delmängder |
kontigent |
skrivskyddad |
format |
---|---|---|---|---|---|---|
|
ja |
ja |
om det behövs |
U |
0 |
ja |
|
ja |
ja |
om det behövs |
U |
1 eller 0 |
ja |
|
ja |
ja |
NULL |
U |
0 |
ja |
|
ja |
ja |
NULL |
U |
1 eller 0 |
ja |
|
ja |
ja |
NULL |
U |
0 |
NULL |
|
ja |
ja |
NULL |
U |
1 eller 0 |
NULL |
|
ja |
NULL |
NULL |
C |
0 |
NULL |
|
ja |
NULL |
NULL |
C |
1 eller 0 |
NULL |
Komplexa matriser¶
NumPy-stil: form och strides¶
Den logiska strukturen för matriser i NumPy-stil definieras av itemsize
, ndim
, shape
och strides
.
Om ndim == 0
, tolkas minnesplatsen som pekas ut av buf
som en skalär av storleken itemsize
. I det fallet är både shape
och strides
NULL
.
Om strides
är NULL
tolkas matrisen som en standard n-dimensionell C-array. Annars måste konsumenten komma åt en n-dimensionell array på följande sätt:
ptr = (char *)buf + index[0] * strides[0] + ... + index[n-1] * strides[n-1];
objekt = *((typeof(objekt) *)ptr);
Som nämnts ovan kan buf
peka på vilken plats som helst inom det faktiska minnesblocket. En exportör kan kontrollera giltigheten av en buffert med denna funktion:
def verify_structure(memlen, itemsize, ndim, shape, strides, offset):
"""Verifiera att parametrarna representerar en giltig array inom
gränserna för det allokerade minnet:
char *mem: början på det fysiska minnesblocket
memlen: längden på det fysiska minnesblocket
offset: (char *)buf - mem
"""
om offset % itemsize:
returnera False
om offset < 0 eller offset+itemsize > memlen:
returnera False
if any(v % itemsize för v i strides):
returnera False
om ndim <= 0:
return ndim == 0 och inte shape och inte strides
om 0 i shape:
returnera True
imin = sum(strides[j]*(shape[j]-1) for j in range(ndim)
om strides[j] <= 0)
imax = summa(strides[j]*(shape[j]-1) for j in range(ndim)
om strides[j] > 0)
return 0 <= offset+imin och offset+imax+itemsize <= memlen
PIL-stil: form, strides och suboffsets¶
Förutom de vanliga elementen kan matriser i PIL-stil innehålla pekare som måste följas för att komma till nästa element i en dimension. Till exempel kan den vanliga tredimensionella C-arrayen char v[2][2][3]
också ses som en array med 2 pekare till 2 tvådimensionella arrayer: char (*v[2])[2][3]
. I suboffsets-representationen kan dessa två pekare bäddas in i början av buf
och peka på två char x[2][3]
-arrayer som kan placeras var som helst i minnet.
Här är en funktion som returnerar en pekare till elementet i en N-D-array som pekas ut av ett N-dimensionellt index när det finns både icke-NULL
strides och suboffsets:
void *get_item_pointer(int ndim, void *buf, Py_ssize_t *strides,
Py_ssize_t *strides, Py_ssize_t *suboffsets, Py_ssize_t *indices) {
char *pointer = (char*)buf;
int i;
for (i = 0; i < ndim; i++) {
pekare += strides[i] * index[i];
om (suboffsets[i] >=0 ) {
pekare = *((char**)pekare) + suboffsets[i];
}
}
return (void*)pekare;
}