简体   繁体   English

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

[英]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.ndarrayctypes属性的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)

Using np.ndarrays as ctypes arguments使用np.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, ...由于这个问题的标题很笼统,...

Constructing a np.ndarray from a ctypes.c_void_p result构建np.ndarrayctypes.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM