[英]How to use NumPy array with ctypes?
I am still writing on a python interface for my c code with ctypes.我仍在使用 ctypes 为我的 c 代码编写 python 接口。 Today I substituted my file reading function with a python version, which was programmed by somebody else using NumPy.
今天,我用 Python 版本替换了我的文件读取功能,该版本是由其他人使用 NumPy 编写的。 The 'old' c version was called with a
byref(p_data)
while p_data=PFloat()
(see below). '旧'c 版本使用
byref(p_data)
p_data=PFloat()
byref(p_data)
而p_data=PFloat()
(见下文)。 The main function takes the p_data
. main 函数采用
p_data
。
Old file reading:旧文件读取:
p_data=POINTER(c_float)
foo.read(filename,byref(p_data))
result=foo.pymain(p_data)
The python file reading function, on the other hand, returns a NumPy array.另一方面,python 文件读取函数返回一个 NumPy 数组。 My question now is:
我现在的问题是:
How do I convert a NumPy array to POINTER(c_float)
?如何将 NumPy 数组转换为
POINTER(c_float)
?
I googled but only found the other way around: C arrays through ctypes accessed as NumPy arrays and things I didn't understand: C-Types Foreign Function Interface (numpy.ctypeslib)我用谷歌搜索,但只找到了另一种方式: 通过 ctypes 访问的 C 数组作为 NumPy 数组和我不明白的事情: C 类型外部函数接口 (numpy.ctypeslib)
[update] corrected a mistake in the example code [更新]更正了示例代码中的一个错误
Your code looks like it has some confusion in it -- ctypes.POINTER()
creates a new ctypes pointer class , not a ctypes instance.您的代码看起来有些混乱
ctypes.POINTER()
创建了一个新的 ctypes 指针类,而不是一个 ctypes 实例。 Anyway, the easiest way to pass a NumPy array to ctypes code is to use the numpy.ndarray
's ctypes
attribute's data_as
method.无论如何,将 NumPy 数组传递给 ctypes 代码的最简单方法是使用
numpy.ndarray
的ctypes
属性的data_as
方法。 Just make sure the underlying data is the right type first.首先确保基础数据是正确的类型。 For example:
例如:
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
as ctypes
argumentsnp.ndarrays
作为ctypes
参数The preferable approach is using ndpointer
, as mentioned in the numpy-docs .更可取的方法是使用
ndpointer
,如numpy-docs 中所述。
This approach is more flexible than using, for example, POINTER(c_double), since several restrictions can be specified, which are verified upon calling the ctypes function.
这种方法比使用例如 POINTER(c_double) 更灵活,因为可以指定几个限制,这些限制在调用 ctypes 函数时进行验证。 These include data type, number of dimensions, shape and flags.
这些包括数据类型、维数、形状和标志。 If a given array does not satisfy the specified restrictions, a TypeError is raised.
如果给定的数组不满足指定的限制,则会引发 TypeError。
Minimal, Reproducible Example最小的、可重现的示例
Calling memcpy from python.从 python 调用memcpy 。 Eventually the filename of the standard C-library
libc.so.6
needs to be adjusted.最终需要调整标准 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)
Output输出
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.]]
If you modify the ndim=1/2
arguments of ndpointer
to be inconsistent with the dimensions of arr_from/arr_to
, the code fails with an ArgumentError
.如果修改了
ndim=1/2
的参数ndpointer
以与的尺寸不一致arr_from/arr_to
,代码失败并ArgumentError
。
As the title of this question is quite general, ...由于这个问题的标题很笼统,...
np.ndarray
from a ctypes.c_void_p
resultnp.ndarray
从ctypes.c_void_p
结果Minimal, Reproducible Example最小的、可重现的示例
In the following example, some memory is allocated by malloc and filled with 0s by memset .在下面的示例中,一些内存由malloc分配,并由memset填充为 0。 Then a numpy array is constructed, to access this memory.
然后构造一个 numpy 数组,以访问该内存。 Of course the occur some ownership issues, as python will not free memory, which was allocated in c.
当然会发生一些所有权问题,因为python不会释放在c中分配的内存。 To avoid memory leaks , one has to free the allocated memory again by ctypes.
为了避免内存泄漏,必须通过 ctypes 再次释放分配的内存。 The copy method can be used for the
np.ndarray
to acquire ownership . 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))
Output输出
<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]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.