Python 如何将NumPy数组与ctypes一起使用?

Python 如何将NumPy数组与ctypes一起使用?,python,numpy,ctypes,Python,Numpy,Ctypes,我仍然在用ctypes为我的c代码编写python接口。今天,我用python版本替换了我的文件读取函数,该版本是由其他人使用NumPy编程的。使用byref(p_数据)调用“旧”c版本,而p_数据=PFloat()(见下文)。主功能获取p\u数据 旧文件读取: p_data=POINTER(c_float) foo.read(filename,byref(p_data)) result=foo.pymain(p_data) 另一方面,python文件读取函数返回一个NumPy数组。我现在的问

我仍然在用ctypes为我的c代码编写python接口。今天,我用python版本替换了我的文件读取函数,该版本是由其他人使用NumPy编程的。使用
byref(p_数据)
调用“旧”c版本,而
p_数据=PFloat()
(见下文)。主功能获取
p\u数据

旧文件读取:

p_data=POINTER(c_float)
foo.read(filename,byref(p_data))
result=foo.pymain(p_data)
另一方面,python文件读取函数返回一个NumPy数组。我现在的问题是:

如何将NumPy数组转换为指针(c_float)

我在谷歌上搜索,但只找到了另一条路:还有一些我不明白的事情:

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

您的代码看起来有些混乱--
ctypes.POINTER()
创建了一个新的ctypes指针类,而不是ctypes实例。无论如何,将NumPy数组传递给ctypes代码的最简单方法是使用
NumPy.ndarray
ctypes
属性的
data\u as
方法。只需首先确保底层数据是正确的类型。例如:

import ctypes
import numpy
c_float_p = ctypes.POINTER(ctypes.c_float)
data = numpy.array([[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]])
data = data.astype(numpy.float32)
data_p = data.ctypes.data_as(c_float_p)
使用
np.ndarrays
作为
ctypes
参数 更好的方法是使用
ndpointer
,如中所述

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

最小、可重复的示例

从python调用。最终需要调整标准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
最小、可重复的示例

在下面的示例中,一些内存由分配,并由0填充。然后构造一个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]
业主(电话:104719884827744):[0]
只是为了好玩:释放一些python内存。。。
arr_noowner(电话:104719884831376):[-768753624381-2851633624381 0]
业主(电话:104719884827744):[-777969624381-28516336243810]

执行上述步骤后,numpy数组是否始终连续?@grabbag数据数组的底层备份内存始终是连续的,因此是的,尽管这可能不是您需要知道的。要以一般方式处理数组,您需要解释
.ctypes
对象的
.shape
.strips
属性,并注意文档中关于
.data
.data\u as()
的注意事项。