バッファプロトコル (buffer Protocol)¶
Pythonで利用可能ないくつかのオブジェクトは、下層にあるメモリ配列またはbuffer へのアクセスを提供します。このようなオブジェクトとして、組み込みのbytes やbytearray 、array.array のようないくつかの拡張型が挙げられます。サードバーティのライブラリは画像処理や数値解析のような特別な目的のために、それら自身の型を定義することができます。
それぞれの型はそれ自身のセマンティクスを持ちますが、おそらく大きなメモリバッファからなるという共通の特徴を共有します。いくつかの状況では仲介するコピーを行うことなく直接バッファにアクセスすることが望まれます。
Python provides such a facility at the C and Python level in the form of thebuffer protocol. This protocol has two sides:
on the producer side, a type can export a "buffer interface" which allowsobjects of that type to expose information about their underlying buffer.This interface is described in the sectionバッファオブジェクト構造体 (buffer object structure); forPython seeEmulating buffer types.
on the consumer side, several means are available to obtain a pointer tothe raw underlying data of an object (for example a method parameter). ForPython see
memoryview.
bytes やbytearray などのシンプルなオブジェクトは、内部のバッファーをバイト列の形式で公開します。バイト列以外の形式も利用可能です。例えば、array.array が公開する要素はマルチバイト値になることがあります。
bufferインターフェースの利用者の一例は、ファイルオブジェクトのwrite() メソッドです: bufferインターフェースを通して一連のバイト列を提供できるどんなオブジェクトでもファイルに書き込むことができます。write() は、その引数として渡されたオブジェクトの内部要素に対する読み出し専用アクセスのみを必要としますが、readinto() のような他のメソッドでは、その引数の内容に対する書き込みアクセスが必要です。bufferインターフェースにより、オブジェクトは読み書き両方、読み出し専用バッファへのアクセスを許可するかそれとも拒否するか選択することができます。
bufferインターフェースの利用者には、対象となるオブジェクトのバッファを得る二つの方法があります:
正しい引数で
PyObject_GetBuffer()を呼び出す;PyArg_ParseTuple()(またはその同族のひとつ) をy*、w*またはs*format codes のいずれかとともに呼び出す。
どちらのケースでも、bufferが必要なくなった時にPyBuffer_Release() を呼び出さなければなりません。これを怠ると、リソースリークのような様々な問題につながる恐れがあります。
Added in version 3.12:The buffer protocol is now accessible in Python, seeEmulating buffer types andmemoryview.
buffer 構造体¶
バッファ構造体(または単純に "buffers")は別のオブジェクトのバイナリデータをPythonプログラマに提供するのに便利です。これはまた、ゼロコピースライシング機構としても使用できます。このメモリブロックを参照する機能を使うことで、どんなデータでもとても簡単にPythonプログラマに提供することができます。メモリは、C 拡張の大きな配列定数かもしれませんし、オペレーティングシステムライブラリに渡す前のメモリブロックかもしれませんし、構造化データをネイティブのインメモリ形式受け渡すのに使用されるかもしれません。
Pythonインタプリタによって提供される多くのデータ型とは異なり、バッファはPyObject ポインタではなく、シンプルなC 構造体です。そのため、作成とコピーが非常に簡単に行えます。バッファの一般的なラッパーが必要なときは、memoryview オブジェクトが作成されます。
エクスポートされるオブジェクトを書く方法の短い説明には、Buffer Object Structures を参照してください。バッファを取得するには、PyObject_GetBuffer() を参照してください。
- typePy_buffer¶
- 次に属します:Stable ABI (すべてのメンバーを含む) (バージョン 3.11 より).
- void*buf¶
バッファフィールドが表している論理構造の先頭を指すポインタ。バッファを提供するオブジェクトの下層物理メモリブロック中のどの位置にもなりえます。例えば
stridesが負だと、この値はメモリブロックの末尾かもしれません。連続 配列の場合この値はメモリブロックの先頭を指します。
- PyObject*obj¶
エクスポート対象オブジェクトへの新しい参照。この参照は消費者によって所有され、自動的に解放されます(つまり、参照カウントが減少します)し、設定されます
NULLbyPyBuffer_Release(). このフィールドは、標準のC-API関数の戻り値に相当します。PyMemoryView_FromBuffer()またはPyBuffer_FillInfo()によってラップされた一時的な バッファである特別なケースでは、このフィールドはNULLです。一般的に、エクスポートオブジェクトはこの方式を使用してはなりません。
- Py_ssize_tlen¶
product(shape)*itemsize。contiguous配列では、下層のメモリブロックの長さになります。非contiguous 配列では、contiguous表現にコピーされた場合に論理構造がもつ長さです。((char*)buf)[0]から((char*)buf)[len-1]の範囲へのアクセスは、連続性 (contiguity) を保証するリクエストによって取得されたバッファに対してのみ許されます。多くの場合に、そのようなリクエストはPyBUF_SIMPLEまたはPyBUF_WRITABLEです。
- intreadonly¶
バッファが読み出し専用であるか示します。このフィールドは
PyBUF_WRITABLEフラグで制御できます。
- Py_ssize_titemsize¶
要素一つ分のbyte単位のサイズ。
struct.calcsize()を非NULLのformat値に対して呼び出した結果と同じです。重要な例外: 消費者が
PyBUF_FORMATフラグを設定することなくバッファを要求した場合、formatはNULLに設定されます。しかしitemsizeは元のフォーマットに従った値を保持します。shapeが存在する場合、product(shape)*itemsize==lenの等式が守られ、利用者はitemsizeを buffer を読むために利用できます。PyBUF_SIMPLEまたはPyBUF_WRITABLEで要求した結果、shapeがNULLであれば、消費者はitemsizeを無視してitemsize==1と見なさなければなりません。
- char*format¶
ANULL terminated string in
structmodule style syntax describingthe contents of a single item. If this isNULL,"B"(unsigned bytes)is assumed.このフィールドは
PyBUF_FORMATフラグによって制御されます。
- intndim¶
The number of dimensions the memory represents as an n-dimensional array.If it is
0,bufpoints to a single item representinga scalar. In this case,shape,stridesandsuboffsetsMUST beNULL.The maximum number of dimensions is given byPyBUF_MAX_NDIM.
- Py_ssize_t*shape¶
メモリ上のN次元配列の形を示す、長さが
ndimであるPy_ssize_tの配列です。shape[0]*...*shape[ndim-1]*itemsizeはlenと等しくなければなりません。shape の値は
shape[n]>=0に制限されます。shape[n]==0の場合に特に注意が必要です。詳細はcomplex arrays を参照してください。shepe (形状) 配列は利用者からは読み出し専用です。
- Py_ssize_t*strides¶
各次元において新しい値を得るためにスキップするバイト数を示す、長さ
ndimのPy_ssize_tの配列。ストライド値は、任意の整数を指定できます。規定の配列では、ストライドは通常でいけば有効です。しかし利用者は、
strides[n]<=0のケースを処理することができる必要があります。詳細についてはcomplex arrays を参照してください。消費者にとって、この strides 配列は読み出し専用です。
- Py_ssize_t*suboffsets¶
Py_ssize_t型の要素を持つ長さndimの配列。suboffsets[n]>=0の場合は、 n 番目の次元に沿って保存されている値はポインタで、 suboffset 値は各ポインタの参照を解決した後に何バイト加えればいいかを示しています。suboffset の値が負の数の場合は、ポインタの参照解決は不要 (連続したメモリブロック内に直接配置されいる) ということになります。全ての suboffset が負数の場合 (つまり参照解決が不要) な場合、このフィールドは
NULL(デフォルト値) でなければなりません。この種の配列表現は Python Imaging Library (PIL) で使われています。このような配列で要素にアクセスする方法についてさらに詳しことはcomplex arrays を参照してください。
消費者にとって、suboffsets 配列は読み出し専用です。
- void*internal¶
バッファを提供する側のオブジェクトが内部的に利用するための変数です。例えば、提供側はこの変数に整数型をキャストして、shape, strides, suboffsets といった配列をバッファを開放するときに同時に解放するべきかどうかを管理するフラグに使うことができるでしょう。バッファを受け取る側は、この値を決して変更してはなりません。
- void*buf¶
Constants:
- PyBUF_MAX_NDIM¶
- 次に属します:Stable ABI (バージョン 3.11 より).
The maximum number of dimensions the memory represents.Exporters MUST respect this limit, consumers of multi-dimensionalbuffers SHOULD be able to handle up to
PyBUF_MAX_NDIMdimensions.Currently set to 64.
バッファリクエストのタイプ¶
バッファは通常、PyObject_GetBuffer() を使うことで、エクスポートするオブジェクトにバッファリクエストを送ることで得られます。メモリの論理的な構造の複雑性は多岐にわたるため、消費者はflags 引数を使って、自身が扱えるバッファの種類を指定します。
Py_buffer の全フィールドは、リクエストの種類によって曖昧さを残さずに定義されます。
リクエストに依存しないフィールド¶
下記のフィールドはflags の影響を受けずに、常に正しい値で設定されます。:obj,buf,len,itemsize,ndim.
readonly, format¶
- PyBUF_WRITABLE¶
- 次に属します:Stable ABI (バージョン 3.11 より).
Controls the
readonlyfield. If set, the exporterMUST provide a writable buffer or else report failure. Otherwise, theexporter MAY provide either a read-only or writable buffer, but the choiceMUST be consistent for all consumers. For example,PyBUF_SIMPLE|PyBUF_WRITABLEcan be used to request a simple writable buffer.
- PyBUF_WRITEABLE¶
This is asoft deprecated alias to
PyBUF_WRITABLE.
- PyBUF_FORMAT¶
- 次に属します:Stable ABI (バージョン 3.11 より).
formatフィールドを制御します。もしフラグが設定されていれば、このフィールドを正しく埋めなければなりません。フラグが設定されていなければ、このフィールドをNULLに設定しなければなりません。
PyBUF_WRITABLE は、次の節に出てくるどのフラグとも | を取ってかまいません。PyBUF_SIMPLE は 0 と定義されているので、PyBUF_WRITABLE は単純な書き込み可能なバッファを要求する単独のフラグとして使えます。
PyBUF_FORMAT must be |'d to any of the flags exceptPyBUF_SIMPLE, becausethe latter already implies formatB (unsigned bytes).PyBUF_FORMAT cannot beused on its own.
shape, strides, suboffsets¶
このフラグは、以下で複雑性が大きい順に並べたメモリの論理的な構造を制御します。個々のフラグは、それより下に記載されたフラグのすべてのビットを含むことに注意してください。
リクエスト | shape | strides | suboffsets |
|---|---|---|---|
| yes | yes | 必要な場合 |
| yes | yes | NULL |
| yes | NULL | NULL |
| NULL | NULL | NULL |
隣接性のリクエスト¶
ストライドの情報があってもなくても、C または Fortran の連続性 が明確に要求される可能性があります。ストライド情報なしに、バッファーは C と隣接している必要があります。
リクエスト | shape | strides | suboffsets | contig |
|---|---|---|---|---|
| yes | yes | NULL | C |
| yes | yes | NULL | F |
| yes | yes | NULL | C か F |
yes | NULL | NULL | C |
複合リクエスト¶
有り得る全てのリクエストの値は、前の節でのフラグの組み合わせで網羅的に定義されています。便利なように、バッファープロトコルでは頻繁に使用される組み合わせを単一のフラグとして提供してます。
次のテーブルのU は連続性が未定義であることを表します。利用者はPyBuffer_IsContiguous() を呼び出して連続性を判定する必要があるでしょう。
リクエスト | shape | strides | suboffsets | contig | readonly | format |
|---|---|---|---|---|---|---|
| yes | yes | 必要な場合 | U | 0 | yes |
| yes | yes | 必要な場合 | U | 1 か 0 | yes |
| yes | yes | NULL | U | 0 | yes |
| yes | yes | NULL | U | 1 か 0 | yes |
| yes | yes | NULL | U | 0 | NULL |
| yes | yes | NULL | U | 1 か 0 | NULL |
| yes | NULL | NULL | C | 0 | NULL |
| yes | 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 はメモリブロック内のどの場所でも指すことが可能です。エクスポーターはこの関数を使用することによってバッファの妥当性を確認出来ます。
defverify_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 """ifoffset%itemsize:returnFalseifoffset<0oroffset+itemsize>memlen:returnFalseifany(v%itemsizeforvinstrides):returnFalseifndim<=0:returnndim==0andnotshapeandnotstridesif0inshape:returnTrueimin=sum(strides[j]*(shape[j]-1)forjinrange(ndim)ifstrides[j]<=0)imax=sum(strides[j]*(shape[j]-1)forjinrange(ndim)ifstrides[j]>0)return0<=offset+iminandoffset+imax+itemsize<=memlen
PIL スタイル: shape, strides, suboffsets¶
PIL スタイルの配列では通常の要素の他に、ある次元の上で次の要素を取得するために辿るポインタを持てます。例えば、通常の3次元 C 配列charv[2][2][3] は、2次元配列への 2 つのポインタからなる配列char(*v[2])[2][3] と見ることもできます。suboffset 表現では、これらの 2 つのポインタはbuf の先頭に埋め込め、メモリのどこにでも配置できる 2 つのcharx[2][3] 配列を指します。
次の例は、 strides も suboffsets もNULL でない場合の、N 次元インデックスによって指されている N 次元配列内の要素へのポインタを返す関数です:
void*get_item_pointer(intndim,void*buf,Py_ssize_t*strides,Py_ssize_t*suboffsets,Py_ssize_t*indices){char*pointer=(char*)buf;inti;for(i=0;i<ndim;i++){pointer+=strides[i]*indices[i];if(suboffsets[i]>=0){pointer=*((char**)pointer)+suboffsets[i];}}return(void*)pointer;}
バッファ関連の関数¶
- intPyObject_CheckBuffer(PyObject*obj)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
obj がbuffer インターフェースをサポートしている場合は
1を返し、そうでない場合は0を返します。1を返したとしても、PyObject_GetBuffer()が成功することは保証されません。この関数は常に成功します。
- intPyObject_GetBuffer(PyObject*exporter,Py_buffer*view,intflags)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
exporter にflags で指定された方法でview を埋めるように要求します。もし exporter が指定されたとおりにバッファを提供できない場合、
BufferErrorを送出し、view->objをNULLに設定した上で、-1を返さなければなりません。成功したときは、view を埋め、
view->objにexporter への新しい参照を設定し、0を返します。チェイン状のバッファプロバイダがリクエストを単一のオブジェクトにリダイレクトするケースでは、view->objはexporter の代わりにこのオブジェクトを参照します (バッファオブジェクト構造体 を参照してください)。malloc()とfree()のように、呼び出しに成功したPyObject_GetBuffer()と対になるPyBuffer_Release()の呼び出しがなけれなればなりません。従って、バッファの利用が済んだらPyBuffer_Release()が厳密に1回だけ呼び出されなければなりません。
- voidPyBuffer_Release(Py_buffer*view)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
Release the bufferview and release thestrong reference(i.e. decrement the reference count) to the view's supporting object,
view->obj. This function MUST be called when the bufferis no longer being used, otherwise reference leaks may occur.PyObject_GetBuffer()を通して取得していないバッファに対してこの関数を呼び出すのは間違いです。
- Py_ssize_tPyBuffer_SizeFromFormat(constchar*format)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
Return the implied
itemsizefromformat.On error, raise an exception and return -1.Added in version 3.9.
- intPyBuffer_IsContiguous(constPy_buffer*view,charorder)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
view で定義されているメモリが、 C スタイル (order ==
'C') のときか、 Fortran スタイル (order =='F')連続 のときか、そのいずれか (order =='A') であれば1を返します。それ以外の場合は0を返します。この関数は常に成功します。
- void*PyBuffer_GetPointer(constPy_buffer*view,constPy_ssize_t*indices)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
与えられたview 内にあるindices が指すメモリ領域を取得します。indices は
view->ndim個のインデックスからなる配列を指していなければなりません。
- intPyBuffer_FromContiguous(constPy_buffer*view,constvoid*buf,Py_ssize_tlen,charfort)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
連続するlen バイトをbuf からview にコピーします。fort には
'C'か'F'を指定できます(それぞれC言語スタイルとFortranスタイルの順序を表します)。成功時には0、エラー時には-1を返します。
- intPyBuffer_ToContiguous(void*buf,constPy_buffer*src,Py_ssize_tlen,charorder)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
src からlen バイトを連続表現でbuf 上にコピーします。order は
'C'または'F'または'A'(C スタイル順序または Fortran スタイル順序またはそれ以外) が指定できます。成功したら0が返り、エラーなら-1が返ります。len !=src->len の場合、この関数は失敗します。
- intPyObject_CopyData(PyObject*dest,PyObject*src)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
Copy data fromsrc todest buffer. Can convert between C-style andor Fortran-style buffers.
成功したら
0が、エラー時には-1が返されます。
- voidPyBuffer_FillContiguousStrides(intndims,Py_ssize_t*shape,Py_ssize_t*strides,intitemsize,charorder)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
strides 配列を、itemsize の大きさの要素がバイト単位の、shape の形をした連続な (order が
'C'なら C-style 、'F'なら Fortran-style の) 多次元配列として埋める。
- intPyBuffer_FillInfo(Py_buffer*view,PyObject*exporter,void*buf,Py_ssize_tlen,intreadonly,intflags)¶
- 次に属します:Stable ABI (バージョン 3.11 より).
サイズがlen のbuf をreadonly に従った書き込み可/不可の設定で公開するバッファリクエストを処理します。buf は符号無しバイトの列として解釈されます。
flags 引数はリクエストのタイプを示します。この関数は、buf が読み出し専用と指定されていて、flags に
PyBUF_WRITABLEが設定されていない限り、常にフラグに指定された通りにview を埋めます。On success, set
view->objto a new reference toexporter andreturn 0. Otherwise, raiseBufferError, setview->objtoNULLand return-1;この関数をgetbufferproc の一部として使う場合には、exporter はエクスポートするオブジェクトに設定しなければならず、さらにflags は変更せずに渡さなければなりません。そうでない場合は、exporter は
NULLでなければなりません。