如何擴(kuò)展NumPy

編寫擴(kuò)展模塊

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

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

上的在線文檔中找到更多信息。

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

必需的子程序

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

一個最小的init{name}方法看起來像:

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

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

int PyModule_AddObjectPyObject

* module ,char * name ,PyObject

* value?

int PyModule_AddIntConstantPyObject

* module ,char * name ,long value?

int PyModule_AddStringConstantPyObject

* module ,char * name ,char * value?

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

定義函數(shù)

傳遞給Py_InitModule函數(shù)的第二個參數(shù)是一個結(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ù)組中的每個條目都是一個PyMethodDef

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

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

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

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

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

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

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


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

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

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

** ** NULLPyArg_ParseTuple Py_XDECREF

** 回來之前。

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

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

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

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

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

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

PyArg_ParseTupleAndKeywords

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

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

引用計數(shù)

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

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

對象的功能)。

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

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

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

引用計數(shù)錯誤的一個常見來源是Py_BuildValue


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

。“O”字符將引用計數(shù)增加1。這將為調(diào)用者提供一個全新數(shù)組的兩個引用計數(shù)。刪除變量并且引用計數(shù)減1時,仍會有額外的引用計數(shù),并且永遠(yuǎn)不會釋放該數(shù)組。您將有一個引用計數(shù)引起的內(nèi)存泄漏。使用'N'字符將避免這種情況,因為它將使用單個引用計數(shù)返回給調(diào)用者一個對象(在元組內(nèi))。

處理數(shù)組對象

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

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

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

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






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

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

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

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

  • obj

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

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

(如果數(shù)據(jù)類型應(yī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

.

或者,可以使用平臺上支持的位寬名稱。例如:

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)可以在不丟失精度的情況下完成時,對象才會轉(zhuǎn)換為所需的類型。
否則將返回 NULL 并引發(fā)錯誤。
在Requirements標(biāo)志中使用 NPY_ARRAY_FORCECAST

、NPY_OUT_ARRAY 、and NPY_ARRAY_INOUT_ARRAY

:

NPY_ARRAY_IN_ARRAY

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

NPY_ARRAY_OUT_ARRAY

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

NPY_ARRAY_INOUT_ARRAY

此標(biāo)志用于指定將用于輸入和輸出的數(shù)組。
必須在接口例程末尾的 Py_DECREF 之前調(diào)用 PyArray_ResolveWritebackIfCopy ,
以將臨時數(shù)據(jù)寫回傳入的原始數(shù)組。
使用 NPY_ARRAY_WRITEBACKIFCOPY
NPY_ARRAY_UPDATEIFCOPY 標(biāo)志要求輸入對象已經(jīng)是數(shù)組(因為其他對象不能以這種方式自動更新)。
如果發(fā)生錯誤,請在設(shè)置了這些標(biāo)志的數(shù)組上使用 PyArray_DiscardWritebackIfCopy

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

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

NPY_ARRAY_FORCECAST

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

NPY_ARRAY_ENSURECOPY

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

NPY_ARRAY_ENSUREARRAY

    • 確保生成的對象是實際的ndarray,而不是子類。

注意

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

,因此NPY_ARRAY_NOTSWAPPED

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

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

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


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

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

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

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

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

(E,i,j,k)。

如前所述,C風(fēng)格的連續(xù)數(shù)組和Fortran風(fēng)格的連續(xù)數(shù)組具有特定的跨步模式。兩個數(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)和()
來測試跨步模式是否匹配標(biāo)準(zhǔn)C或FortranPyArray_ISFORTRAN

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

示例

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

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


微信公眾號:Java大數(shù)據(jù)與數(shù)據(jù)倉庫