2010-12-04 92 views
22

我正在使用Python(通過​​)包裝C庫來運行一系列計算。在運行的不同階段,我想將數據導入Python,特別是數組。從ctypes數組獲取數據到numpy

我使用的包裝做了兩種不同類型的返回數組數據(這是特別感興趣的是我)的:

  • ​​陣列:當我做type(x)(其中x是​​數組,我得到的回報一個<class 'module_name.wrapper_class_name.c_double_Array_12000'>我知道這個數據是從文檔內部數據的副本,我能夠把它變成一個numpy陣列容易:

    >>> np.ctypeslib.as_array(x) 
    

這將返回一個1D numpy數組數組。

  • ctype指針數據:在這種情況下,從庫中的文檔,我明白,我得到一個指針存儲並直接使用庫中的數據。乳清我做type(y)(其中y是指針)我得到<class 'module_name.wrapper_class_name.LP_c_double'>。有了這個情況下,我仍然能夠通過像y[0][2]數據索引,但我通過一個超級尷尬才能夠讓它進入numpy的:

    >>> np.frombuffer(np.core.multiarray.int_asbuffer(
        ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize)) 
    

我發現這在一箇舊numpy郵件列表thread from Travis Oliphant ,但不在numpy文檔中。如果不是這樣的做法我嘗試如上我得到以下幾點:

>>> np.ctypeslib.as_array(y) 
... 
... BUNCH OF STACK INFORMATION 
... 
AttributeError: 'LP_c_double' object has no attribute '__array_interface__' 

這是np.frombuffer方法來做到這一點的最好或唯一途徑?我願意接受其他建議,但必須仍然希望使用numpy,因爲我有許多其他後處理代碼依賴於numpy功能,我想使用此數據

+0

你有控制輸出是否是C lib?你可以改變圖書館的API嗎? – 2010-12-04 20:03:09

+0

是的 - 我有消息來源。我不確定要走哪條路,因爲指針方法允許Python直接對數據進行操作,我認爲在某些情況下這可能是一個優勢。在我的情況下,是的,將所有內容作爲`ctype`數組出現將是一個優勢。任何建議? – dtlussier 2010-12-04 20:09:49

+1

我建議讓庫使用你在Python中分配的(NumPy-)數組並傳遞給庫。這樣,你可以採取相同的記憶,但你不必費心去做任何尷尬的轉換。你已經有了一個NumPy數組,並且將它傳遞給一個庫可以通過使用[`numpy.ctypeslib.ndpointer`]得到很好的支持(http://docs.scipy.org/doc/numpy/reference/routines.ctypeslib.html #numpy.ctypeslib.ndpointer)作爲參數類型傳遞給函數的ctypes包裝。 (如果這不清楚,只是問...) – 2010-12-04 20:46:16

回答

23

從ctypes指針對象創建NumPy數組是一個有問題的操作。目前還不清楚誰實際擁有指針指向的內存。它什麼時候會再次釋放?它有效多久?只要有可能,我會盡量避免這種構造。在Python代碼中創建數組並將它們傳遞給C函數比使用由Python不知道的C函數分配的內存要容易和安全。通過做後者,你在某種程度上否定了使用高級語言來處理內存管理的好處。

如果您確定有人照看內存,您可以創建一個暴露Python「緩衝區協議」的對象,然後使用此緩衝區對象創建一個NumPy數組。你給創建在您的文章緩衝對象的一種方式,通過無證int_asbuffer()功能:

buffer = numpy.core.multiarray.int_asbuffer(
    ctypes.addressof(y.contents), 8*array_length) 

(請注意,我取代8np.dtype(float).itemsize它總是8,在任何平臺上。)一種不同的方式來創建緩衝對象將是經由ctypes的調用從Python C API的PyBuffer_FromMemory()功能:

buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory 
buffer_from_memory.restype = ctypes.py_object 
buffer = buffer_from_memory(y, 8*array_length) 

對於這兩種方法,可以通過

a = numpy.frombuffer(buffer, float) 
創建從 buffer一個NumPy的陣列

(其實我不明白你爲什麼使用.astype(),而不是第二個參數來frombuffer的;此外,我不知道爲什麼你使用np.int,而你前面的數組包含double s表示。)

恐怕不會比這更容易,但這並不壞,你不覺得嗎?你可以將所有醜陋的細節都埋在一個包裝函數中,不用再擔心了。

7

另一種可能性(可能需要比編寫第一個答案時可用的庫更新的版本 - 我測試了與ctypes 1.1.0numpy 1.5.0b2類似的東西)是將指針轉換爲數組。

np.ctypeslib.as_array(
    (ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents))) 

這似乎仍然有共享所有權的語義,所以你可能需要確保你釋放潛在的緩衝區最終。

5

這些都不在Python 3爲我工作作爲一個ctypes指針轉換成numpy的ndarray在python 2和3的一般解決方案,我發現這個工作(通過得到一個只讀緩衝液):

def make_nd_array(c_pointer, shape, dtype=np.float64, order='C', own_data=True): 
    arr_size = np.prod(shape[:]) * np.dtype(dtype).itemsize 
    if sys.version_info.major >= 3: 
     buf_from_mem = ctypes.pythonapi.PyMemoryView_FromMemory 
     buf_from_mem.restype = ctypes.py_object 
     buf_from_mem.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int) 
     buffer = buf_from_mem(c_pointer, arr_size, 0x100) 
    else: 
     buf_from_mem = ctypes.pythonapi.PyBuffer_FromMemory 
     buf_from_mem.restype = ctypes.py_object 
     buffer = buf_from_mem(c_pointer, arr_size) 
    arr = np.ndarray(tuple(shape[:]), dtype, buffer, order=order) 
    if own_data and not arr.flags.owndata: 
     return arr.copy() 
    else: 
     return arr 
0

如果你都OK使用Python創建陣列,用二維數組下面的示例工作在python3:

import numpy as np 
import ctypes 

OutType = (ctypes.c_float * 4) * 6 
out = OutType() 
YourCfunction = ctypes.CDLL('./yourlib.so').voidreturningfunctionwithweirdname 
YourCfunction.argtypes = [ctypes.POINTER(ctypes.c_float)]*3, ctypes.POINTER(ctypes.c_float)]*5, OutType] 
YourCfunction(input1, input2, out) 
out = np.array(out) # convert it to numpy 

print(out) 

numpy的和ctypes的版本是1.11.1和1.1.0