2010-12-04 97 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