从ctypes数组获取数据到numpy
我正在使用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
功能,我想使用此数据。
从ctypes指针对象创建NumPy数组是一个有问题的操作。目前还不清楚谁实际拥有指针指向的内存。它什么时候会再次释放?它有效多久?只要有可能,我会尽量避免这种构造。在Python代码中创建数组并将它们传递给C函数比使用由Python不知道的C函数分配的内存要容易和安全。通过做后者,你在某种程度上否定了使用高级语言来处理内存管理的好处。
如果您确定有人照看内存,您可以创建一个暴露Python“缓冲区协议”的对象,然后使用此缓冲区对象创建一个NumPy数组。你给创建在您的文章缓冲对象的一种方式,通过无证int_asbuffer()
功能:
buffer = numpy.core.multiarray.int_asbuffer(
ctypes.addressof(y.contents), 8*array_length)
(请注意,我取代8
为np.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表示。)
恐怕不会比这更容易,但这并不坏,你不觉得吗?你可以将所有丑陋的细节都埋在一个包装函数中,不用再担心了。
另一种可能性(可能需要比编写第一个答案时可用的库更新的版本 - 我测试了与ctypes 1.1.0
和numpy 1.5.0b2
类似的东西)是将指针转换为数组。
np.ctypeslib.as_array(
(ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents)))
这似乎仍然有共享所有权的语义,所以你可能需要确保你释放潜在的缓冲区最终。
这些都不在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
如果你都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
你有控制输出是否是C lib?你可以改变图书馆的API吗? – 2010-12-04 20:03:09
是的 - 我有消息来源。我不确定要走哪条路,因为指针方法允许Python直接对数据进行操作,我认为在某些情况下这可能是一个优势。在我的情况下,是的,将所有内容作为`ctype`数组出现将是一个优势。任何建议? – dtlussier 2010-12-04 20:09:49
我建议让库使用你在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