如何擴(kuò)展NumPy

編寫(xiě)擴(kuò)展模塊

雖然ndarray對(duì)象旨在允許在Python中進(jìn)行快速計(jì)算,但它也被設(shè)計(jì)為通用的并且滿(mǎn)足各種各樣的計(jì)算需求。因此,如果絕對(duì)速度是必不可少的,那么就不能替換特定于您的應(yīng)用程序和硬件的精心編制的循環(huán)。這是numpy包含f2py的原因之一,因此可以使用易于使用的機(jī)制將(簡(jiǎn)單的)C / C ++和(任意)Fortran代碼直接鏈接到Python中。我們鼓勵(lì)您使用和改進(jìn)此機(jī)制。本節(jié)的目的不是記錄此工具,而是記錄編寫(xiě)此工具所依賴(lài)的擴(kuò)展模塊的更基本步驟。

當(dāng)擴(kuò)展模塊被編寫(xiě),編譯并安裝到Python路徑(sys.path)中的某個(gè)位置時(shí),可以將代碼導(dǎo)入到Python中,就好像它是標(biāo)準(zhǔn)的python文件一樣。它將包含已在C代碼中定義和編譯的對(duì)象和方法。在Python中執(zhí)行此操作的基本步驟已有詳細(xì)記錄,您可以在www.python.org

上的在線(xiàn)文檔中找到更多信息。

除了Python C-API之外,NumPy還有一個(gè)完整而豐富的C-API,允許在C級(jí)上進(jìn)行復(fù)雜的操作。但是,對(duì)于大多數(shù)應(yīng)用程序,通常只使用少量API調(diào)用。如果你需要做的就是提取一個(gè)指向內(nèi)存的指針以及一些形狀信息以傳遞給另一個(gè)計(jì)算例程,那么你將使用非常不同的調(diào)用,然后如果你試圖創(chuàng)建一個(gè)類(lèi)似于數(shù)組的新類(lèi)型或添加一個(gè)新數(shù)據(jù)ndarrays的類(lèi)型。本章介紹了最常用的API調(diào)用和宏。

必需的子程序

必須在C代碼中定義一個(gè)函數(shù)才能使Python將其用作擴(kuò)展模塊。該函數(shù)必須被稱(chēng)為init {name},其中{name}是Python中模塊的名稱(chēng)。必須聲明此函數(shù),以便對(duì)例程外部的代碼可見(jiàn)。除了添加您想要的方法和常量之外,此子例程還必須包含調(diào)用import_array()
和/或import_ufunc()取決于需要哪個(gè)C-API。只要實(shí)際調(diào)用任何C-API子例程,忘記放置這些命令就會(huì)將自身顯示為一個(gè)丑陋的分段錯(cuò)誤(崩潰)。實(shí)際上,在單個(gè)文件中可以有多個(gè)init {name}函數(shù),在這種情況下,該文件將定義多個(gè)模塊。但是,有一些技巧可以讓它正常工作,這里沒(méi)有涉及。

一個(gè)最小的init{name}方法看起來(lái)像:

PyMODINIT_FUNC
init{name}(void)
{
   (void)Py_InitModule({name}, mymethods);
   import_array();
}

mymethods必須是PyMethodDef結(jié)構(gòu)的數(shù)組(通常是靜態(tài)聲明的),它包含方法名,實(shí)際的C函數(shù),指示方法是否使用關(guān)鍵字參數(shù)的變量,以及docstrings。這些將在下一節(jié)中介紹。如果要向模塊添加常量,則存儲(chǔ)Py_InitModule的返回值,Py_InitModule是一個(gè)模塊對(duì)象。向模塊添加項(xiàng)目的最常用方法是使用PyModule_GetDict(模塊)獲取模塊字典。使用模塊字典,您可以手動(dòng)將任何您喜歡的內(nèi)容添加到模塊中。向模塊添加對(duì)象的更簡(jiǎn)單方法是使用三個(gè)額外的Python C-API調(diào)用之一,這些調(diào)用不需要單獨(dú)提取模塊字典。這些內(nèi)容記錄在Python文檔中,但為方便起見(jiàn),在此處重復(fù):

int PyModule_AddObjectPyObject

* module ,char * namePyObject

* value?

int PyModule_AddIntConstantPyObject

* module ,char * name ,long value?

int PyModule_AddStringConstantPyObject

* module ,char * name ,char * value?

所有這三個(gè)函數(shù)都需要 模塊 對(duì)象(Py_InitModule的返回值)。該 名稱(chēng) 是標(biāo)簽?zāi)K中的值的字符串。根據(jù)調(diào)用的函數(shù), value 參數(shù)是一般對(duì)象(PyModule_AddObject竊取對(duì)它的引用),整數(shù)常量或字符串常量。

定義函數(shù)

傳遞給Py_InitModule函數(shù)的第二個(gè)參數(shù)是一個(gè)結(jié)構(gòu),可以很容易地在模塊中定義函數(shù)。在上面給出的示例中,mymethods結(jié)構(gòu)將在文件的早期(通常在init {name}子例程之前)定義為:

static PyMethodDef mymethods[] = {
    { nokeywordfunc,nokeyword_cfunc,
      METH_VARARGS,
      Doc string},
    { keywordfunc, keyword_cfunc,
      METH_VARARGS|METH_KEYWORDS,
      Doc string},
    {NULL, NULL, 0, NULL} /* Sentinel */
}

mymethods數(shù)組中的每個(gè)條目都是一個(gè)PyMethodDef

結(jié)構(gòu),包含1)Python名稱(chēng),2)實(shí)現(xiàn)函數(shù)的C函數(shù),3)指示是否接受此函數(shù)的關(guān)鍵字的標(biāo)志,以及4)函數(shù)的文檔字符串。通過(guò)向該表添加更多條目,可以為單個(gè)模塊定義任意數(shù)量的功能。最后一個(gè)條目必須全部為NULL,如圖所示充當(dāng)哨兵。Python查找此條目以了解已定義模塊的所有函數(shù)。

完成擴(kuò)展模塊必須做的最后一件事是實(shí)際編寫(xiě)執(zhí)行所需功能的代碼。有兩種函數(shù):不接受關(guān)鍵字參數(shù)的函數(shù)和那些函數(shù)。

沒(méi)有關(guān)鍵字參數(shù)的函數(shù)

不接受關(guān)鍵字參數(shù)的函數(shù)應(yīng)寫(xiě)為:

static PyObject*
nokeyword_cfunc (PyObject *dummy, PyObject *args)
{
    /* convert Python arguments */
    /* do function */
    /* return something */
}

偽參數(shù)不在此上下文中使用,可以安全地忽略。該 ARGS 參數(shù)包含所有的傳遞給函數(shù)作為一個(gè)元組的參數(shù)。此時(shí)您可以執(zhí)行任何操作,但通常管理輸入?yún)?shù)的最簡(jiǎn)單方法是調(diào)用PyArg_ParseTuple

(args,format_string,addresses_to_C_variables ...)或PyArg_UnpackTuple (元組,“名稱(chēng)”,分鐘,最大,......)。有關(guān)如何使用第一個(gè)函數(shù)的詳細(xì)說(shuō)明,請(qǐng)參見(jiàn)Python C-API參考手冊(cè)第5.5節(jié)(解析參數(shù)和構(gòu)建值)。您應(yīng)該特別注意使用轉(zhuǎn)換器函數(shù)在Python對(duì)象和C對(duì)象之間進(jìn)行的“O&”格式。所有其他格式函數(shù)都可以(大部分)被認(rèn)為是這個(gè)一般規(guī)則的特殊情況。NumPy C-API中定義了幾種可能有用的轉(zhuǎn)換器功能。特別是,該PyArray_DescrConverter
函數(shù)對(duì)于支持任意數(shù)據(jù)類(lèi)型規(guī)范非常有用。此函數(shù)將任何有效的數(shù)據(jù)類(lèi)型Python對(duì)象轉(zhuǎn)換為
對(duì)象。請(qǐng)記住傳入應(yīng)填寫(xiě)的C變量的地址。PyArray_Descr * 有很多關(guān)于如何在PyArg_ParseTuple


整個(gè)NumPy源代碼中使用的示例。標(biāo)準(zhǔn)用法是這樣的:

PyObject *input;
PyArray_Descr *dtype;
if (!PyArg_ParseTuple(args, "OO&", &input,
                      PyArray_DescrConverter,
                      &dtype)) return NULL;

請(qǐng)務(wù)必記住,在使用“O”格式字符串時(shí),您會(huì)獲得對(duì)該對(duì)象的 借用 引用。但是,轉(zhuǎn)換器功能通常需要某種形式的內(nèi)存處理。在此示例中,如果轉(zhuǎn)換成功,則 dtype 將保持對(duì)對(duì)象的新引用,而 輸入 將保留借用的引用。因此,如果此轉(zhuǎn)換與另一個(gè)轉(zhuǎn)換(比如整數(shù))混合并且數(shù)據(jù)類(lèi)型轉(zhuǎn)換成功但整數(shù)轉(zhuǎn)換失敗,那么您需要在返回之前將引用計(jì)數(shù)釋放到數(shù)據(jù)類(lèi)型對(duì)象。一種典型的方法是
在調(diào)用之前將 dtype 設(shè)置為,然后
dtype上 使用PyArray_Descr *

** ** NULLPyArg_ParseTuple Py_XDECREF

** 回來(lái)之前。

處理完輸入?yún)?shù)后,將編寫(xiě)實(shí)際完成工作的代碼(可能會(huì)根據(jù)需要調(diào)用其他函數(shù))。C函數(shù)的最后一步是返回一些東西。如果遇到錯(cuò)誤,NULL則應(yīng)返回(確保實(shí)際設(shè)置了錯(cuò)誤)。如果不應(yīng)返回任何內(nèi)容,則遞增
Py_None

并返回它。如果應(yīng)該返回單個(gè)對(duì)象,則返回它(確保您首先擁有對(duì)它的引用)。如果應(yīng)該返回多個(gè)對(duì)象,那么您需要返回一個(gè)元組。該Py_BuildValue (format_string,c_variables ...)函數(shù)可以很容易地從C變量構(gòu)建Python對(duì)象的元組。請(qǐng)?zhí)貏e注意格式字符串中“N”和“O”之間的區(qū)別,否則您可能很容易造成內(nèi)存泄漏。'O'格式字符串增加它對(duì)應(yīng)的C變量的引用計(jì)數(shù),而'N'格式字符串竊取對(duì)相應(yīng)C變量的引用。如果已經(jīng)為對(duì)象創(chuàng)建了引用并且只想對(duì)元組進(jìn)行引用,則應(yīng)使用“N”。如果您只有一個(gè)對(duì)象的借用引用并且需要?jiǎng)?chuàng)建一個(gè)來(lái)提供元組,則應(yīng)該使用“O”。PyObject * PyObject *

帶關(guān)鍵字參數(shù)的函數(shù)

這些函數(shù)與沒(méi)有關(guān)鍵字參數(shù)的函數(shù)非常相似。唯一的區(qū)別是函數(shù)簽名是:

static PyObject*
keyword_cfunc (PyObject *dummy, PyObject *args, PyObject *kwds)
{
...
}

kwds參數(shù)包含一個(gè)Python字典,其鍵是關(guān)鍵字參數(shù)的名稱(chēng),其值是相應(yīng)的關(guān)鍵字參數(shù)值。無(wú)論你認(rèn)為合適,都可以處理這本字典。然而,處理它的最簡(jiǎn)單方法是PyArg_ParseTuple

PyArg_ParseTupleAndKeywords

(args,kwds,format_string,char * kwlist [],地址......)調(diào)用替換
(args,format_string,addresses ...)函數(shù)。此函數(shù)的kwlist參數(shù)是一個(gè)NULL字符串?dāng)?shù)組,提供了預(yù)期的關(guān)鍵字參數(shù)。format_string中的每個(gè)條目都應(yīng)該有一個(gè)字符串。如果傳入無(wú)效的關(guān)鍵字參數(shù),則使用此函數(shù)將引發(fā)TypeError。

有關(guān)此功能的更多幫助,請(qǐng)參閱Python文檔中的擴(kuò)展和嵌入教程的第1.8節(jié)(擴(kuò)展函數(shù)的關(guān)鍵字參數(shù))。

引用計(jì)數(shù)

編寫(xiě)擴(kuò)展模塊時(shí)最大的困難是引用計(jì)數(shù)。這是f2py,weave,Cython,ctypes等受歡迎的重要原因。如果您錯(cuò)誤處理引用計(jì)數(shù),則可能會(huì)出現(xiàn)從內(nèi)存泄漏到分段錯(cuò)誤的問(wèn)題。我知道處理參考計(jì)數(shù)的唯一策略是血液,汗水和眼淚。首先,你強(qiáng)迫每個(gè)Python變量都有一個(gè)引用計(jì)數(shù)。然后,您可以準(zhǔn)確了解每個(gè)函數(shù)對(duì)對(duì)象的引用計(jì)數(shù)的作用,以便在需要時(shí)可以正確使用DECREF和INCREF。引用計(jì)數(shù)可以真正測(cè)試您對(duì)編程工藝的耐心和勤奮程度。盡管形象嚴(yán)峻,大多數(shù)引用計(jì)數(shù)的情況非常簡(jiǎn)單,最常見(jiàn)的困難是由于某些錯(cuò)誤而在從例程退出之前不在對(duì)象上使用DECREF。第二,是不會(huì)在傳遞給將要竊取引用的函數(shù)或宏的對(duì)象上擁有引用的常見(jiàn)錯(cuò)誤( 例如 PyTuple_SET_ITEM

,和大多數(shù)采取PyArray_Descr

對(duì)象的功能)。

通常,在創(chuàng)建變量時(shí)會(huì)獲得對(duì)變量的新引用,或者是某個(gè)函數(shù)的返回值(但是有一些突出的例外 - 例如從元組或字典中獲取項(xiàng)目)。當(dāng)您擁有引用時(shí),您有責(zé)任確保Py_DECREF

在不再需要該變量時(shí)調(diào)用(var)(并且沒(méi)有其他函數(shù)“竊取”其引用)。此外,如果您將Python對(duì)象傳遞給將“竊取”引用的函數(shù),那么您需要確保擁有它(或用于Py_INCREF 獲取自己的引用)。您還將遇到借用參考的概念。借用引用的函數(shù)不會(huì)改變對(duì)象的引用計(jì)數(shù),也不會(huì)期望“保持”引用。它只是暫時(shí)使用該對(duì)象。當(dāng)你使用PyArg_ParseTuple 或者
PyArg_UnpackTuple

您收到對(duì)元組中對(duì)象的借用引用,不應(yīng)更改其函數(shù)內(nèi)的引用計(jì)數(shù)。通過(guò)練習(xí),您可以學(xué)會(huì)正確引用計(jì)數(shù),但一開(kāi)始可能會(huì)令人沮喪。

引用計(jì)數(shù)錯(cuò)誤的一個(gè)常見(jiàn)來(lái)源是Py_BuildValue


函數(shù)。請(qǐng)?zhí)貏e注意'N'格式字符和'O'格式字符之間的區(qū)別。如果在子例程中創(chuàng)建一個(gè)新對(duì)象(例如輸出數(shù)組),并且在返回值的元組中將其傳回,則最應(yīng)該使用“N”格式字符。Py_BuildValue

?!癘”字符將引用計(jì)數(shù)增加1。這將為調(diào)用者提供一個(gè)全新數(shù)組的兩個(gè)引用計(jì)數(shù)。刪除變量并且引用計(jì)數(shù)減1時(shí),仍會(huì)有額外的引用計(jì)數(shù),并且永遠(yuǎn)不會(huì)釋放該數(shù)組。您將有一個(gè)引用計(jì)數(shù)引起的內(nèi)存泄漏。使用'N'字符將避免這種情況,因?yàn)樗鼘⑹褂脝蝹€(gè)引用計(jì)數(shù)返回給調(diào)用者一個(gè)對(duì)象(在元組內(nèi))。

處理數(shù)組對(duì)象

NumPy的大多數(shù)擴(kuò)展模塊都需要訪(fǎng)問(wèn)ndarray對(duì)象(或其中一個(gè)子類(lèi))的內(nèi)存。最簡(jiǎn)單的方法不需要您了解NumPy的內(nèi)部結(jié)構(gòu)。方法是

  1. 確保處理的是行為良好的數(shù)組(按機(jī)器字節(jié)順序和單段對(duì)齊),具有正確的維數(shù)類(lèi)型和數(shù)量。
    1. 通過(guò)使用PyArray_FromAny或在其上構(gòu)建的宏將其從某個(gè)Python對(duì)象轉(zhuǎn)換。
    2. 通過(guò)使用PyArray_NewFromDescr或基于它的更簡(jiǎn)單的宏或函數(shù)構(gòu)建所需形狀和類(lèi)型的新ndarray。
  2. 獲取數(shù)組的形狀和指向其實(shí)際數(shù)據(jù)的指針。
  3. 將數(shù)據(jù)和形狀信息傳遞給子例程或?qū)嶋H執(zhí)行計(jì)算的其他代碼部分。
  4. 如果您正在編寫(xiě)算法,那么我建議您使用數(shù)組中包含的步幅信息來(lái)訪(fǎng)問(wèn)數(shù)組的元素(PyArray_GetPtr宏使這一過(guò)程變得輕松)。然后,您可以放松您的要求,這樣就不會(huì)強(qiáng)制使用單段數(shù)組和可能導(dǎo)致的數(shù)據(jù)復(fù)制。

這些子主題中的每一個(gè)都會(huì)在下面的小節(jié)中介紹。

轉(zhuǎn)換任意序列對(duì)象






從任何可以轉(zhuǎn)換為數(shù)組的Python對(duì)象獲取數(shù)組的主程序是PyArray_FromAny

。這個(gè)函數(shù)非常靈活,有許多輸入?yún)?shù)。幾個(gè)宏使它更容易使用基本功能。PyArray_FROM_OTF

可以說(shuō)是最常用的這些宏中最有用的。它允許您將任意Python對(duì)象轉(zhuǎn)換為特定內(nèi)置數(shù)據(jù)類(lèi)型( 例如 float)的數(shù)組,同時(shí)指定一組特定的需求( 例如, 連續(xù),對(duì)齊和可寫(xiě))。語(yǔ)法是

從任何可以轉(zhuǎn)換為數(shù)組的 Python 對(duì)象 obj 返回 ndarray。返回?cái)?shù)組中的維數(shù)由對(duì)象確定。
返回?cái)?shù)組的所需數(shù)據(jù)類(lèi)型在 typenum 中提供,它應(yīng)該是枚舉類(lèi)型之一。
對(duì)返回?cái)?shù)組的*要求(requirements)*可以是標(biāo)準(zhǔn)數(shù)組標(biāo)志的任意組合。下面將更詳細(xì)地解釋這些論點(diǎn)中的每一個(gè)。
成功后,您將收到對(duì)數(shù)組的新引用。如果失敗,則返回 NULL 并設(shè)置異常。

  • obj

    該對(duì)象可以是任何可轉(zhuǎn)換為ndarray的Python對(duì)象。
    如果對(duì)象已經(jīng)是滿(mǎn)足要求的ndarray的子類(lèi),則返回一個(gè)新的引用。否則,構(gòu)造一個(gè)新的數(shù)組。
    除非使用數(shù)組接口,否則將obj的內(nèi)容復(fù)制到新數(shù)組,以便不必復(fù)制數(shù)據(jù)。
    可以轉(zhuǎn)換為數(shù)組的對(duì)象包括:
    1)任何嵌套的Sequence對(duì)象,
    2)暴露數(shù)組接口的任何對(duì)象,
    3)具有數(shù)組方法的任何對(duì)象(應(yīng)該返回ndarray),
    以及4)任何標(biāo)量對(duì)象(變成零維數(shù)組)。
    否則符合要求的ndarray的子類(lèi)將被傳遞。如果要確?;?lèi)ndarray,
    則在Requirements標(biāo)志中使用 NPY_ARRAY_ENSUREARRAY

。
只有在必要時(shí)才會(huì)制作副本。
如果要保證復(fù)制,則將 NPY_ARRAY_ENSURECOPY

(如果數(shù)據(jù)類(lèi)型應(yīng)從對(duì)象本身確定)??梢允褂没贑的名稱(chēng):

NPY_BOOL

, NPY_BYTE , NPY_UBYTE , NPY_SHORT , NPY_USHORT , NPY_INT , NPY_UINT , NPY_LONG , NPY_ULONG , NPY_LONGLONG , NPY_ULONGLONG , NPY_DOUBLE , NPY_LONGDOUBLE , NPY_CFLOAT , NPY_CDOUBLE , NPY_CLONGDOUBLE , NPY_OBJECT

.

或者,可以使用平臺(tái)上支持的位寬名稱(chēng)。例如:

NPY_INT8

, NPY_INT16 , NPY_INT32 , NPY_INT64 , NPY_UINT8 , NPY_UINT16 , NPY_UINT32 , NPY_UINT64 , NPY_FLOAT32 , NPY_FLOAT64 , NPY_COMPLEX64 , NPY_COMPLEX128

.

僅當(dāng)可以在不丟失精度的情況下完成時(shí),對(duì)象才會(huì)轉(zhuǎn)換為所需的類(lèi)型。
否則將返回 NULL 并引發(fā)錯(cuò)誤。
在Requirements標(biāo)志中使用 NPY_ARRAY_FORCECAST

、NPY_OUT_ARRAY 、and NPY_ARRAY_INOUT_ARRAY

:

NPY_ARRAY_IN_ARRAY

此標(biāo)志對(duì)于必須按C連續(xù)順序和對(duì)齊的數(shù)組非常有用。這些類(lèi)型的數(shù)組通常是某些算法的輸入數(shù)組。

NPY_ARRAY_OUT_ARRAY

此標(biāo)志用于指定C連續(xù)順序的數(shù)組,該數(shù)組是對(duì)齊的,并且也可以寫(xiě)入。這樣的數(shù)組通常作為輸出返回(盡管通常這樣的輸出數(shù)組是從頭開(kāi)始創(chuàng)建的)。

NPY_ARRAY_INOUT_ARRAY

此標(biāo)志用于指定將用于輸入和輸出的數(shù)組。
必須在接口例程末尾的 Py_DECREF 之前調(diào)用 PyArray_ResolveWritebackIfCopy ,
以將臨時(shí)數(shù)據(jù)寫(xiě)回傳入的原始數(shù)組。
使用 NPY_ARRAY_WRITEBACKIFCOPY
NPY_ARRAY_UPDATEIFCOPY 標(biāo)志要求輸入對(duì)象已經(jīng)是數(shù)組(因?yàn)槠渌麑?duì)象不能以這種方式自動(dòng)更新)。
如果發(fā)生錯(cuò)誤,請(qǐng)?jiān)谠O(shè)置了這些標(biāo)志的數(shù)組上使用 PyArray_DiscardWritebackIfCopy

(obj)。
這將設(shè)置底層基本數(shù)組可寫(xiě),而不會(huì)導(dǎo)致內(nèi)容復(fù)制回原始數(shù)組。

可以作為附加要求進(jìn)行OR運(yùn)算的其他有用標(biāo)志包括:

NPY_ARRAY_FORCECAST

強(qiáng)制轉(zhuǎn)換為所需的類(lèi)型,即使在不丟失信息的情況下也是如此。

NPY_ARRAY_ENSURECOPY

確保生成的數(shù)組是原始數(shù)組的副本。

NPY_ARRAY_ENSUREARRAY

    • 確保生成的對(duì)象是實(shí)際的ndarray,而不是子類(lèi)。

注意

數(shù)組是否進(jìn)行字節(jié)交換取決于數(shù)組的數(shù)據(jù)類(lèi)型。始終請(qǐng)求本機(jī)字節(jié)順序數(shù)組PyArray_FROM_OTF

,因此NPY_ARRAY_NOTSWAPPED

require參數(shù)中不需要標(biāo)志。也無(wú)法從此例程中獲取字節(jié)交換數(shù)組。

創(chuàng)建一個(gè)全新的ndarray

通常,必須在擴(kuò)展模塊代碼中創(chuàng)建新數(shù)組。也許需要輸出數(shù)組,
并且您不希望調(diào)用者必須提供它。
也許只需要一個(gè)臨時(shí)數(shù)組來(lái)進(jìn)行中間計(jì)算。
無(wú)論需要什么,都需要簡(jiǎn)單的方法來(lái)獲得任何數(shù)據(jù)類(lèi)型的ndarray對(duì)象。
這樣做最常見(jiàn)的功能是PyArray_NewFromDescr

。
所有數(shù)組創(chuàng)建函數(shù)都經(jīng)過(guò)這個(gè)重復(fù)使用的代碼。由于其靈活性,使用起來(lái)可能有點(diǎn)混亂。
結(jié)果,存在更易于使用的更簡(jiǎn)單的形式。這些表單是PyArray_SimpleNew

函數(shù)族的一部分
,它通過(guò)為常見(jiàn)用例提供默認(rèn)值來(lái)簡(jiǎn)化界面。

獲取ndarray內(nèi)存并訪(fǎng)問(wèn)ndarray的元素

如果obj是一個(gè) ndarray(),那么ndarray 的數(shù)據(jù)區(qū)域由void 指針(obj)或char 指針(obj)指向。請(qǐng)記?。ㄍǔ#┐藬?shù)據(jù)區(qū)域可能未根據(jù)數(shù)據(jù)類(lèi)型對(duì)齊,它可能表示字節(jié)交換數(shù)據(jù),和/或可能無(wú)法寫(xiě)入。如果數(shù)據(jù)區(qū)域是對(duì)齊的并且是以本機(jī)字節(jié)順序排列的,那么如何獲取數(shù)組的特定元素只能由npy_intp變量數(shù)組(obj)確定。特別是,這個(gè)整數(shù)的c數(shù)組顯示了必須向當(dāng)前元素指針添加多少字節(jié)才能到達(dá)每個(gè)維度中的下一個(gè)元素。對(duì)于小于4維的數(shù)組,有PyArrayObject *

PyArray_DATA PyArray_BYTES PyArray_STRIDES **PyArray_GETPTR{k}
(obj,...)宏,其中{k}是整數(shù)1,2,3或4,這使得使用數(shù)組步幅更容易。爭(zhēng)論...... 將{k}非負(fù)整數(shù)索引表示到數(shù)組中。例如,假設(shè)E是一個(gè)三維的ndarray。E[i,j,k]
獲得元素的(void *)指針作為PyArray_GETPTR3

(E,i,j,k)。

如前所述,C風(fēng)格的連續(xù)數(shù)組和Fortran風(fēng)格的連續(xù)數(shù)組具有特定的跨步模式。兩個(gè)數(shù)組標(biāo)志(NPY_ARRAY_C_CONTIGUOUS

NPY_ARRAY_F_CONTIGUOUS )表示特定數(shù)組的跨步模式是否與C風(fēng)格的連續(xù)或Fortran風(fēng)格的連續(xù)匹配或兩者都不匹配??梢允褂?a rel="noopener noreferrer" target="_blank" data-original-title="" title="">PyArray_IS_C_CONTIGUOUS (obj)和()
來(lái)測(cè)試跨步模式是否匹配標(biāo)準(zhǔn)C或FortranPyArray_ISFORTRAN

(obj)分別。大多數(shù)第三方庫(kù)都期望連續(xù)的數(shù)組。但是,通常支持通用跨越并不困難。我鼓勵(lì)您盡可能在自己的代碼中使用跨步信息,并保留包裹第三方代碼的單段要求。使用與ndarray一起提供的跨步信息而不是需要連續(xù)的跨步減少了必須進(jìn)行的復(fù)制。

示例

以下示例顯示了如何編寫(xiě)一個(gè)包含兩個(gè)輸入?yún)?shù)(將轉(zhuǎn)換為數(shù)組)和輸出參數(shù)(必須是數(shù)組)的包裝器。該函數(shù)返回None并更新輸出數(shù)組。請(qǐng)注意NumPy v1.14及更高版本的WRITEBACKIFCOPY語(yǔ)義的更新使用

static PyObject *
example_wrapper(PyObject *dummy, PyObject *args)
{
    PyObject *arg1=NULL, *arg2=NULL, *out=NULL;
    PyObject *arr1=NULL, *arr2=NULL, *oarr=NULL;

    if (!PyArg_ParseTuple(args, "OOO!", &arg1, &arg2,
        &PyArray_Type, &out)) return NULL;

    arr1 = PyArray_FROM_OTF(arg1, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
    if (arr1 == NULL) return NULL;
    arr2 = PyArray_FROM_OTF(arg2, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
    if (arr2 == NULL) goto fail;
#if NPY_API_VERSION >= 0x0000000c
    oarr = PyArray_FROM_OTF(out, NPY_DOUBLE, NPY_ARRAY_INOUT_ARRAY2);
#else
    oarr = PyArray_FROM_OTF(out, NPY_DOUBLE, NPY_ARRAY_INOUT_ARRAY);
#endif
    if (oarr == NULL) goto fail;

    /* code that makes use of arguments */
    /* You will probably need at least
       nd = PyArray_NDIM(<..>)    -- number of dimensions
       dims = PyArray_DIMS(<..>)  -- npy_intp array of length nd
                                     showing length in each dim.
       dptr = (double *)PyArray_DATA(<..>) -- pointer to data.

       If an error occurs goto fail.
     */

    Py_DECREF(arr1);
    Py_DECREF(arr2);
#if NPY_API_VERSION >= 0x0000000c
    PyArray_ResolveWritebackIfCopy(oarr);
#endif
    Py_DECREF(oarr);
    Py_INCREF(Py_None);
    return Py_None;

 fail:
    Py_XDECREF(arr1);
    Py_XDECREF(arr2);
#if NPY_API_VERSION >= 0x0000000c
    PyArray_DiscardWritebackIfCopy(oarr);
#endif
    Py_XDECREF(oarr);
    return NULL;
}

作者:柯廣的網(wǎng)絡(luò)日志 ? 如何擴(kuò)展NumPy


微信公眾號(hào):Java大數(shù)據(jù)與數(shù)據(jù)倉(cāng)庫(kù)