陣列介面協定#

注意

此頁面描述 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)。請注意,這些整數可能大於平台 intlong 可以容納的範圍(Python int 是 C long)。使用此屬性的程式碼有責任適當地處理此問題;可以透過在可能發生溢位時引發錯誤,或使用 long long 作為形狀的 C 型別。

typestr (必需)

一個字串,提供同質陣列的基本型別。基本字串格式包含 3 個部分:描述資料位元組順序的字元 (<: 小端序, >: 大端序, |: 不相關), 給出陣列基本型別的字元程式碼,以及提供型別使用的位元組數的整數。

基本型別字元程式碼為

t

位元欄位(後面的整數給出位元欄位中的位元數)。

b

布林值(整數型別,其中所有值僅為 TrueFalse

i

整數

u

無號整數

f

浮點數

c

複數浮點數

m

時間差

M

日期時間

O

物件(即記憶體包含指向 PyObject 的指標)

S

字串(固定長度的字元序列)

U

Unicode(固定長度的 Py_UCS4 序列)

V

其他(void * – 每個項目都是固定大小的記憶體區塊)

descr (可選)

提供同質陣列中每個項目記憶體佈局更詳細描述的元組列表。列表中的每個元組都有兩個或三個元素。通常,當 typestrV[0-9]+ 時會使用此屬性,但這不是必需的。唯一的要求是 typestr 鍵中表示的位元組數與此處表示的總位元組數相同。目的是支援組成陣列元素的類似 C 結構的描述。列表中每個元組的元素為

  1. 一個字串,提供與此資料型別部分相關聯的名稱。這也可以是 ('完整名稱', '基本名稱') 的元組,其中基本名稱將是表示欄位完整名稱的有效 Python 變數名稱。

  2. typestr 中的基本型別描述字串或另一個列表(用於巢狀結構化型別)

  3. 一個可選的形狀元組,提供應重複此結構部分多少次。如果未給出,則假定不重複。可以使用此通用介面描述非常複雜的結構。但是請注意,陣列的每個元素仍然是相同的資料型別。下面給出了一些使用此介面的範例。

預設值: [('', typestr)]

data (可選)

一個 2 元組,其第一個引數是一個 Python 整數,指向儲存陣列內容的資料區域。

注意

從 C/C++ 透過 PyLong_From* 或 Cython 或 pybind11 等高階綁定轉換時,請確保使用位元數足夠大的整數。

此指標必須指向資料的第一個元素(換句話說,在這種情況下始終忽略任何偏移量)。元組中的第二個條目是唯讀標誌(true 表示資料區域是唯讀的)。

此屬性也可以是一個公開 緩衝區介面 的物件,該介面將用於共享資料。如果此鍵不存在(或傳回 None),則記憶體共享將透過物件本身的緩衝區介面完成。在這種情況下,可以使用 offset 鍵來指示緩衝區的開始位置。如果要保護記憶體區域,則新物件必須儲存對公開陣列介面的物件的參考。

預設值: None

strides (可選)

可以是 None,表示 C 風格的連續陣列,也可以是 strides 元組,它提供跳到相應維度中的下一個陣列元素所需的位元組數。每個條目都必須是一個整數(Python int)。與 shape 一樣,這些值可能大於 C intlong 可以表示的值;呼叫程式碼應適當地處理此問題,可以透過引發錯誤,或在 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 介面非常相似。差異主要在於美觀。特別是

  1. PyArrayInterface 結構在末尾沒有 descr 成員(因此也沒有標誌 ARR_HAS_DESCR)

  2. __array_struct__ 傳回的 PyCapsulecontext 成員(正式名稱為 PyCObjectdesc 成員)未指定。通常,它是公開陣列的物件(以便可以保留對它的參考並在銷毀 C 物件時銷毀)。現在明確要求以某種方式使用此欄位來保存對擁有物件的參考。

    注意

    直到 2020 年 8 月,它都說

    現在它必須是一個元組,其第一個元素是一個帶有 “PyArrayInterface Version #” 的字串,其第二個元素是公開陣列的物件。

    此設計在提出後幾乎立即被撤回,在 <https://mail.python.org/pipermail/numpy-discussion/2006-June/020995.html> 中。儘管有 14 年的文件與此相反,但在任何時候都不能假定 __array_interface__ 膠囊持有此元組內容。

  3. __array_interface__['data'] 傳回的元組過去是一個十六進位字串(現在它是一個整數或長整數)。

  4. 沒有 __array_interface__ 屬性,而是 __array_interface__ 字典中的所有鍵(版本除外)都是它們自己的屬性:因此,要取得 Python 端資訊,您必須分別存取屬性

    • __array_data__

    • __array_shape__

    • __array_strides__

    • __array_typestr__

    • __array_descr__

    • __array_offset__

    • __array_mask__