Python 将数据从ctypes数组获取到numpy

Python 将数据从ctypes数组获取到numpy,python,numpy,ctypes,Python,Numpy,Ctypes,我正在使用Python(通过ctypes)包装的C库来运行一系列计算。在运行的不同阶段,我希望将数据输入Python,特别是numpy数组 我使用的包装对数组数据执行两种不同类型的返回(我特别感兴趣): ctypesArray:当我输入类型(x)(其中x是ctypes数组,我得到一个作为回报。我知道该数据是文档中内部数据的副本,我能够轻松地将其放入numpy数组中: >>> np.ctypeslib.as_array(x) 这将返回数据的1Dnumpy数组 ctype

我正在使用Python(通过
ctypes
)包装的C库来运行一系列计算。在运行的不同阶段,我希望将数据输入Python,特别是
numpy
数组

我使用的包装对数组数据执行两种不同类型的返回(我特别感兴趣):

  • ctypes
    Array
    :当我输入
    类型(x)
    (其中x是
    ctypes
    数组,我得到一个
    作为回报。我知道该数据是文档中内部数据的副本,我能够轻松地将其放入
    numpy
    数组中:

    >>> np.ctypeslib.as_array(x)
    
这将返回数据的1D
numpy
数组

  • ctype
    指向数据的指针
    :在这种情况下,从库的文档中,我了解到我得到了一个指向存储并直接用于库的数据的指针。我怎么做
    键入(y)
    (其中y是指针)我得到了
    。在这种情况下,我仍然能够通过
    y[0][2]
    之类的数据进行索引,但我只能通过一个超级笨拙的:

    >>> np.frombuffer(np.core.multiarray.int_asbuffer(
        ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize))
    
我在一个旧的
numpy
邮件列表中发现了这一点,但在
numpy
文档中没有发现。如果不使用上述方法,我会得到以下结果:

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

这是
np。frombuffer
方法是实现这一点的最佳方法还是唯一方法?我愿意接受其他建议,但必须仍然希望使用
numpy
,因为我有许多其他后处理代码依赖于
numpy
功能,我想使用这些功能从ctype创建numpy数组s pointer对象是一个有问题的操作。不清楚指针指向的内存究竟归谁所有。它什么时候会被释放?有效期有多长?只要有可能,我都会尽量避免这种构造。在Python代码中创建数组并将其传递给C函数比使用mem更容易、更安全通过执行后一种操作,您在某种程度上否定了由高级语言负责内存管理的优势

import ctypes
import numpy as np

n_bytes_int = 4
size = 7

clib = ctypes.cdll.LoadLibrary("libc.so.6")

clib.malloc.argtypes = [ctypes.c_size_t]
clib.malloc.restype = ctypes.c_void_p

clib.memset.argtypes = [
    ctypes.c_void_p,
    ctypes.c_int,
    ctypes.c_size_t]
clib.memset.restype = np.ctypeslib.ndpointer(
    dtype=np.int32, ndim=1, flags='C_CONTIGUOUS')

clib.free.argtypes = [ctypes.c_void_p]
clib.free.restype = ctypes.c_void_p


pntr = clib.malloc(size * n_bytes_int)
ndpntr = clib.memset(pntr, 0, size * n_bytes_int)
print(type(ndpntr))
ctypes_pntr = ctypes.cast(ndpntr, ctypes.POINTER(ctypes.c_int))
print(type(ctypes_pntr))
print()
arr_noowner = np.ctypeslib.as_array(ctypes_pntr, shape=(size,))
arr_owner = np.ctypeslib.as_array(ctypes_pntr, shape=(size,)).copy()
# arr_owner = arr_noowner.copy()


print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))

print('\nfree allocated memory again ...\n')
_ = clib.free(pntr)

print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))

print('\njust for fun: free some python-memory ...\n')
_ = clib.free(arr_owner.ctypes.data_as(ctypes.c_void_p))

print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))
如果您确实确定有人负责内存,您可以创建一个公开Python“缓冲协议”的对象,然后使用此缓冲区对象创建一个NumPy数组。您在文章中给出了一种创建缓冲区对象的方法,通过未记录的
int\u 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\u FromMemory()
函数:

buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object
buffer = buffer_from_memory(y, 8*array_length)
对于这两种方法,您都可以通过以下方式从
buffer
创建NumPy数组

a = numpy.frombuffer(buffer, float)
(实际上,我不明白为什么您使用
.astype()
而不是
frombuffer
的第二个参数;此外,我想知道为什么您使用
np.int
,而您之前说数组包含
s。)

恐怕不会比这容易得多,但也没那么糟糕,你不觉得吗?你可以把所有难看的细节都隐藏在包装函数中,不用再担心了。

另一种可能性(可能需要比第一个答案编写时更新版本的库——我测试了类似于
cTypes1.1.0
numpy1.5.0b2
)的东西)是从指针转换到数组

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

这似乎仍然具有共享所有权语义,因此您可能需要确保最终释放底层缓冲区。

在Python 3中,这两种方法都不适用于我。在Python 2和3中,作为将ctypes指针转换为numpy ndarray的通用解决方案,我发现这是可行的(通过获取只读缓冲区):


如果您可以在python中创建数组,则以下2d数组示例在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_arr = (c_float * 8)()
 np.ctypeslib.as_array(c_arr)
从指针

 c_arr = (c_float * 8)()
 ptr = ctypes.pointer(c_arr[0])
 np.ctypeslib.as_array(ptr, shape=(8,))
使用
np.ndarrays
作为
ctypes
参数 更好的方法是使用
ndpointer
,如中所述

这种方法比使用更灵活,例如, 指针(c_double),因为可以指定多个限制 在调用ctypes函数时验证。这些包括数据 类型、维度数、形状和标志。如果给定数组没有 如果满足指定的限制,将引发TypeError

最小、可重复的示例

最终需要调整标准C库libc.so.6的文件名

import ctypes
import numpy as np

n_bytes_f64 = 8
nrows = 2
ncols = 5

clib = ctypes.cdll.LoadLibrary("libc.so.6")

clib.memcpy.argtypes = [
    np.ctypeslib.ndpointer(dtype=np.float64, ndim=2, flags='C_CONTIGUOUS'),
    np.ctypeslib.ndpointer(dtype=np.float64, ndim=1, flags='C_CONTIGUOUS'),
    ctypes.c_size_t]
clib.memcpy.restype = ctypes.c_void_p

arr_from = np.arange(nrows * ncols).astype(np.float64)
arr_to = np.empty(shape=(nrows, ncols), dtype=np.float64)

print('arr_from:', arr_from)
print('arr_to:', arr_to)

print('\ncalling clib.memcpy ...\n')
clib.memcpy(arr_to, arr_from, nrows * ncols * n_bytes_f64)

print('arr_from:', arr_from)
print('arr_to:', arr_to)
输出

arr_from: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
arr_to: [[0.0e+000 4.9e-324 9.9e-324 1.5e-323 2.0e-323]
 [2.5e-323 3.0e-323 3.5e-323 4.0e-323 4.4e-323]]

calling clib.memcpy ...

arr_from: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
arr_to: [[0. 1. 2. 3. 4.]
 [5. 6. 7. 8. 9.]]
<class 'numpy.ctypeslib.ndpointer_<i4_1d_C_CONTIGUOUS'>
<class '__main__.LP_c_int'>

arr_noowner (at 104719884831376): [0 0 0 0 0 0 0]
arr_owner (at 104719884827744): [0 0 0 0 0 0 0]

free allocated memory again ...

arr_noowner (at 104719884831376): [ -7687536     24381 -28516336     24381         0         0         0]
arr_owner (at 104719884827744): [0 0 0 0 0 0 0]

just for fun: free some python-memory ...

arr_noowner (at 104719884831376): [ -7687536     24381 -28516336     24381         0         0         0]
arr_owner (at 104719884827744): [ -7779696     24381 -28516336     24381         0         0         0]
如果将
ndpointer
ndim=1/2
参数修改为与
arr\u从/arr\u到
的维度不一致,则代码将失败,并出现
ArgumentError

由于这个问题的题目很笼统

ctypes.c\u void\u p
结果构造
np.ndarray
最小、可重复的示例

在下面的示例中,一些内存由分配并填充0s by。然后构造一个numpy数组来访问此内存。当然,会出现一些所有权问题,因为python不会释放在c中分配的内存。为了避免内存泄漏,必须通过ctypes再次访问分配的内存。方法可以是用于
np.ndarray
获取所有权

import ctypes
import numpy as np

n_bytes_int = 4
size = 7

clib = ctypes.cdll.LoadLibrary("libc.so.6")

clib.malloc.argtypes = [ctypes.c_size_t]
clib.malloc.restype = ctypes.c_void_p

clib.memset.argtypes = [
    ctypes.c_void_p,
    ctypes.c_int,
    ctypes.c_size_t]
clib.memset.restype = np.ctypeslib.ndpointer(
    dtype=np.int32, ndim=1, flags='C_CONTIGUOUS')

clib.free.argtypes = [ctypes.c_void_p]
clib.free.restype = ctypes.c_void_p


pntr = clib.malloc(size * n_bytes_int)
ndpntr = clib.memset(pntr, 0, size * n_bytes_int)
print(type(ndpntr))
ctypes_pntr = ctypes.cast(ndpntr, ctypes.POINTER(ctypes.c_int))
print(type(ctypes_pntr))
print()
arr_noowner = np.ctypeslib.as_array(ctypes_pntr, shape=(size,))
arr_owner = np.ctypeslib.as_array(ctypes_pntr, shape=(size,)).copy()
# arr_owner = arr_noowner.copy()


print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))

print('\nfree allocated memory again ...\n')
_ = clib.free(pntr)

print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))

print('\njust for fun: free some python-memory ...\n')
_ = clib.free(arr_owner.ctypes.data_as(ctypes.c_void_p))

print('arr_noowner (at {:}): {:}'.format(arr_noowner.ctypes.data, arr_noowner))
print('arr_owner (at {:}): {:}'.format(arr_owner.ctypes.data, arr_owner))
输出

arr_from: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
arr_to: [[0.0e+000 4.9e-324 9.9e-324 1.5e-323 2.0e-323]
 [2.5e-323 3.0e-323 3.5e-323 4.0e-323 4.4e-323]]

calling clib.memcpy ...

arr_from: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
arr_to: [[0. 1. 2. 3. 4.]
 [5. 6. 7. 8. 9.]]
<class 'numpy.ctypeslib.ndpointer_<i4_1d_C_CONTIGUOUS'>
<class '__main__.LP_c_int'>

arr_noowner (at 104719884831376): [0 0 0 0 0 0 0]
arr_owner (at 104719884827744): [0 0 0 0 0 0 0]

free allocated memory again ...

arr_noowner (at 104719884831376): [ -7687536     24381 -28516336     24381         0         0         0]
arr_owner (at 104719884827744): [0 0 0 0 0 0 0]

just for fun: free some python-memory ...

arr_noowner (at 104719884831376): [ -7687536     24381 -28516336     24381         0         0         0]
arr_owner (at 104719884827744): [ -7779696     24381 -28516336     24381         0         0         0]

arr_noowner(电话:104719884831376):[0]
业主(电话:104719884827744):[0]
再次释放分配的内存。。。
arr_noowner(电话:104719884831376):[-768753624381-2851633624381 0