陣列介面協定#
注意
此頁面描述 NumPy 特定的 API,用於從其他 C 擴充功能存取 NumPy 陣列的內容。PEP 3118 – 修訂後的緩衝區協定
為 Python 2.6 和 3.0 引入了類似的標準化 API,供任何擴充模組使用。Cython 的緩衝區陣列支援使用 PEP 3118 API;請參閱 Cython NumPy 教學。Cython 提供了一種編寫程式碼的方式,該程式碼支援比 2.6 更舊的 Python 版本的緩衝區協定,因為它具有向後相容的實作,利用了此處描述的陣列介面。
- 版本:
3
陣列介面(有時稱為陣列協定)於 2005 年建立,作為類似陣列的 Python 物件在可能的情況下智能地重複使用彼此的資料緩衝區的一種手段。同質 N 維陣列介面是物件共享 N 維陣列記憶體和資訊的預設機制。該介面由 Python 端和 C 端組成,使用兩個屬性。希望在應用程式碼中被視為 N 維陣列的物件應至少支援這些屬性之一。希望在應用程式碼中支援 N 維陣列的物件應尋找至少這些屬性之一,並適當地使用提供的資訊。
此介面描述了同質陣列,因為陣列的每個項目都具有相同的「型別」。此型別可以非常簡單,也可以是非常任意且複雜的類似 C 的結構。
有兩種使用介面的方式:Python 端和 C 端。兩者都是獨立的屬性。
Python 端#
此介面方法包括物件具有 __array_interface__
屬性。
- object.__array_interface__#
包含項目的字典(3 個必需項和 5 個可選項)。字典中的可選鍵如果未提供,則具有隱含的預設值。
鍵為
- shape (必需)
元組,其元素是每個維度中的陣列大小。每個條目都是一個整數(Python
int
)。請注意,這些整數可能大於平台int
或long
可以容納的範圍(Pythonint
是 Clong
)。使用此屬性的程式碼有責任適當地處理此問題;可以透過在可能發生溢位時引發錯誤,或使用long long
作為形狀的 C 型別。- typestr (必需)
一個字串,提供同質陣列的基本型別。基本字串格式包含 3 個部分:描述資料位元組順序的字元 (
<
: 小端序,>
: 大端序,|
: 不相關), 給出陣列基本型別的字元程式碼,以及提供型別使用的位元組數的整數。基本型別字元程式碼為
- descr (可選)
提供同質陣列中每個項目記憶體佈局更詳細描述的元組列表。列表中的每個元組都有兩個或三個元素。通常,當 typestr 為
V[0-9]+
時會使用此屬性,但這不是必需的。唯一的要求是 typestr 鍵中表示的位元組數與此處表示的總位元組數相同。目的是支援組成陣列元素的類似 C 結構的描述。列表中每個元組的元素為一個字串,提供與此資料型別部分相關聯的名稱。這也可以是
('完整名稱', '基本名稱')
的元組,其中基本名稱將是表示欄位完整名稱的有效 Python 變數名稱。typestr 中的基本型別描述字串或另一個列表(用於巢狀結構化型別)
一個可選的形狀元組,提供應重複此結構部分多少次。如果未給出,則假定不重複。可以使用此通用介面描述非常複雜的結構。但是請注意,陣列的每個元素仍然是相同的資料型別。下面給出了一些使用此介面的範例。
預設值:
[('', typestr)]
- data (可選)
一個 2 元組,其第一個引數是一個 Python 整數,指向儲存陣列內容的資料區域。
注意
從 C/C++ 透過
PyLong_From*
或 Cython 或 pybind11 等高階綁定轉換時,請確保使用位元數足夠大的整數。此指標必須指向資料的第一個元素(換句話說,在這種情況下始終忽略任何偏移量)。元組中的第二個條目是唯讀標誌(true 表示資料區域是唯讀的)。
此屬性也可以是一個公開 緩衝區介面 的物件,該介面將用於共享資料。如果此鍵不存在(或傳回 None),則記憶體共享將透過物件本身的緩衝區介面完成。在這種情況下,可以使用 offset 鍵來指示緩衝區的開始位置。如果要保護記憶體區域,則新物件必須儲存對公開陣列介面的物件的參考。
預設值:
None
- strides (可選)
可以是
None
,表示 C 風格的連續陣列,也可以是 strides 元組,它提供跳到相應維度中的下一個陣列元素所需的位元組數。每個條目都必須是一個整數(Pythonint
)。與 shape 一樣,這些值可能大於 Cint
或long
可以表示的值;呼叫程式碼應適當地處理此問題,可以透過引發錯誤,或在 C 中使用long long
。預設值為None
,表示 C 風格的連續記憶體緩衝區。在此模型中,陣列的最後一個維度變化最快。例如,對於陣列條目長度為 8 個位元組且形狀為(10, 20, 30)
的物件,預設 strides 元組將為(4800, 240, 8)
。預設值:
None
(C 風格的連續)- mask (可選)
None
或公開陣列介面的物件。遮罩陣列的所有元素都應僅解釋為 true 或 not true,表示此陣列的哪些元素有效。此物件的形狀應與原始陣列的形狀 “可廣播”。預設值:
None
(所有陣列值均有效)- offset (可選)
陣列資料區域的整數偏移量。僅當 data 為
None
或傳回memoryview
物件時,才能使用此選項。預設值:
0
。- version (必需)
一個整數,顯示介面的版本(即此版本為 3)。請注意不要使用它來使公開未來版本介面的物件失效。
C 結構存取#
這種陣列介面方法允許僅使用一個屬性查找和一個定義明確的 C 結構來更快地存取陣列。
- object.__array_struct__#
一個
PyCapsule
,其pointer
成員包含指向已填寫的PyArrayInterface
結構的指標。結構的記憶體是動態建立的,並且PyCapsule
也是使用適當的解構函式建立的,因此此屬性的檢索器只需在完成時將Py_DECREF
應用於此屬性傳回的物件即可。此外,資料需要複製出來,或必須持有對公開此屬性的物件的參考,以確保資料不會被釋放。公開__array_struct__
介面的物件也不得在其記憶體被其他物件參考時重新分配其記憶體。
PyArrayInterface
結構在 numpy/ndarrayobject.h
中定義為
typedef struct {
int two; /* contains the integer 2 -- simple sanity check */
int nd; /* number of dimensions */
char typekind; /* kind in array --- character code of typestr */
int itemsize; /* size of each element */
int flags; /* flags indicating how the data should be interpreted */
/* must set ARR_HAS_DESCR bit to validate descr */
Py_ssize_t *shape; /* A length-nd array of shape information */
Py_ssize_t *strides; /* A length-nd array of stride information */
void *data; /* A pointer to the first element of the array */
PyObject *descr; /* NULL or data-description (same as descr key
of __array_interface__) -- must set ARR_HAS_DESCR
flag or this will be ignored. */
} PyArrayInterface;
flags 成員可能包含 5 個位元,顯示應如何解釋資料,以及 1 個位元,顯示應如何解釋介面。資料位元為 NPY_ARRAY_C_CONTIGUOUS
(0x1)、NPY_ARRAY_F_CONTIGUOUS
(0x2)、NPY_ARRAY_ALIGNED
(0x100)、NPY_ARRAY_NOTSWAPPED
(0x200) 和 NPY_ARRAY_WRITEABLE
(0x400)。最後一個標誌 NPY_ARR_HAS_DESCR
(0x800) 指示此結構是否具有 arrdescr 欄位。除非存在此標誌,否則不應存取該欄位。
-
NPY_ARR_HAS_DESCR#
自 2006 年 6 月 16 日起新增
過去,大多數實作都使用 PyCObject
(現在為 PyCapsule
)的 desc
成員本身(不要將其與上面 PyArrayInterface
結構的 “descr” 成員混淆 — 它們是兩個獨立的事物)來保存指向公開介面的物件的指標。現在,這是介面的明確部分。務必取得對物件的參考,並在傳回 PyCapsule
之前呼叫 PyCapsule_SetContext
,並配置解構函式以遞減此參考。
注意
__array_struct__
被認為是舊版,不應在新程式碼中使用。請改用 緩衝區協定 或 DLPack 協定 numpy.from_dlpack
。
型別描述範例#
為了清楚起見,提供一些型別描述和對應的 __array_interface__
‘descr’ 條目的範例很有用。感謝 Scott Gilbert 提供這些範例
在每種情況下,‘descr’ 鍵都是可選的,但當然提供了更多資訊,這些資訊對於各種應用程式可能很重要
* Float data
typestr == '>f4'
descr == [('','>f4')]
* Complex double
typestr == '>c8'
descr == [('real','>f4'), ('imag','>f4')]
* RGB Pixel data
typestr == '|V3'
descr == [('r','|u1'), ('g','|u1'), ('b','|u1')]
* Mixed endian (weird but could happen).
typestr == '|V8' (or '>u8')
descr == [('big','>i4'), ('little','<i4')]
* Nested structure
struct {
int ival;
struct {
unsigned short sval;
unsigned char bval;
unsigned char cval;
} sub;
}
typestr == '|V8' (or '<u8' if you want)
descr == [('ival','<i4'), ('sub', [('sval','<u2'), ('bval','|u1'), ('cval','|u1') ]) ]
* Nested array
struct {
int ival;
double data[16*4];
}
typestr == '|V516'
descr == [('ival','>i4'), ('data','>f8',(16,4))]
* Padded structure
struct {
int ival;
double dval;
}
typestr == '|V16'
descr == [('ival','>i4'),('','|V4'),('dval','>f8')]
應該清楚的是,可以使用此介面描述任何結構化型別。
與陣列介面(版本 2)的差異#
版本 2 介面非常相似。差異主要在於美觀。特別是
PyArrayInterface 結構在末尾沒有 descr 成員(因此也沒有標誌 ARR_HAS_DESCR)
從
__array_struct__
傳回的PyCapsule
的context
成員(正式名稱為PyCObject
的desc
成員)未指定。通常,它是公開陣列的物件(以便可以保留對它的參考並在銷毀 C 物件時銷毀)。現在明確要求以某種方式使用此欄位來保存對擁有物件的參考。注意
直到 2020 年 8 月,它都說
現在它必須是一個元組,其第一個元素是一個帶有 “PyArrayInterface Version #” 的字串,其第二個元素是公開陣列的物件。
此設計在提出後幾乎立即被撤回,在 <https://mail.python.org/pipermail/numpy-discussion/2006-June/020995.html> 中。儘管有 14 年的文件與此相反,但在任何時候都不能假定
__array_interface__
膠囊持有此元組內容。從
__array_interface__['data']
傳回的元組過去是一個十六進位字串(現在它是一個整數或長整數)。沒有
__array_interface__
屬性,而是__array_interface__
字典中的所有鍵(版本除外)都是它們自己的屬性:因此,要取得 Python 端資訊,您必須分別存取屬性__array_data__
__array_shape__
__array_strides__
__array_typestr__
__array_descr__
__array_offset__
__array_mask__