如何使用带有ctypes的NumPy数组?

时间:2010-07-07 14:22:44

标签: python numpy ctypes

我仍然在使用ctypes编写c代码的python接口。今天我用python版本替换了我的文件读取功能,这是由其他人使用NumPy编写的。使用byref(p_data)调用'old'c版本,而p_data = PFloat()(见下文)。主函数采用p_data。

旧文件阅读:

p_data=POINTER(c_float)
foo.read(filename,byref(p_data))
result=foo.pymain(p_data)

另一方面,python文件读取函数返回NumPy数组。我现在的问题是:

如何将NumPy数组转换为POINTER(c_float)?

我用谷歌搜索,但只发现了相反:C arrays through ctypes accessed as NumPy arrays和我不理解的事情:C-Types Foreign Function Interface (numpy.ctypeslib)

[更新] 纠正了示例代码中的错误

2 个答案:

答案 0 :(得分:19)

你的代码看起来有些混乱 - ctypes.POINTER()创建一个新的ctypes指针 class ,而不是ctypes实例。无论如何,将NumPy数组传递给ctypes代码的最简单方法是使用numpy.ndarray的{​​{1}}属性的ctypes方法。只需先确保基础数据是正确的类型。例如:

data_as

答案 1 :(得分:4)

使用np.ndarrays作为ctypes参数

更可取的方法是使用numpy-docs中提到的ndpointer

这种方法比使用例如 POINTER(c_double),因为可以指定几个限制,所以 在调用ctypes函数时被验证。这些包括数据 类型,尺寸数量,形状和标志。如果给定的数组没有 满足指定的限制,将引发TypeError。

最小的可复制示例

从python调用memcpy。最终,需要调整标准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.]]

如果将ndim=1/2的{​​{1}}参数修改为与ndpointer的尺寸不一致,则代码将失败,并显示arr_from/arr_to

这个问题的标题很笼统,...

根据ArgumentError结果构建np.ndarray

最小的可复制示例

在以下示例中,某些内存由malloc分配,并由memset填充0。然后构造一个numpy数组,以访问该内存。当然会发生一些所有权问题,因为python不会释放在c中分配的内存。为了避免内存泄漏,必须再次free通过ctypes分配内存。 ctypes.c_void_p可以使用copy方法来获取所有权

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))