【问题标题】:How to use NumPy array with ctypes?如何将 NumPy 数组与 ctypes 一起使用?
【发布时间】:2010-07-07 14:22:44
【问题描述】:

我仍在使用 ctypes 为我的 c 代码编写 python 接口。今天我用 python 版本替换了我的文件读取功能,它是由其他人使用 NumPy 编写的。 '旧' c 版本用byref(p_data)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)

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

【问题讨论】:

    标签: python numpy ctypes


    【解决方案1】:

    您的代码看起来有些混乱——ctypes.POINTER() 创建了一个新的 ctypes 指针 class,而不是 ctypes 实例。无论如何,将 NumPy 数组传递给 ctypes 代码的最简单方法是使用numpy.ndarrayctypes 属性的data_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)
    

    【讨论】:

    • 做完上述步骤后,numpy数组是否总是连续的?
    • @grabbag ndarray 的底层后备内存始终是连续的,所以是的,尽管这可能不是您需要知道的。要一般处理数组,您需要解释.ctypes 对象的.shape.strides 属性,并注意文档中.data.data_as() 的注意事项。 docs.scipy.org/doc/numpy/reference/generated/…
    【解决方案2】:

    使用np.ndarrays 作为ctypes 参数

    最好的方法是使用ndpointer,如numpy-docs 中所述。

    这种方法比使用更灵活,例如, 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.]]
    

    如果将ndpointerndim=1/2 参数修改为与arr_from/arr_to 的尺寸不一致,则代码将失败并返回ArgumentError

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

    ctypes.c_void_p 结果构造np.ndarray

    最小的、可重现的示例

    在下面的例子中,malloc 分配了一些内存,memset 用 0 填充。然后构造一个 numpy 数组,以访问该内存。当然会发生一些所有权问题,因为 python 不会释放在 c 中分配的内存。为了避免内存泄漏,必须通过 ctypes 再次free 分配的内存。 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))
    

    输出

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

    【讨论】:

      猜你喜欢
      • 2010-12-09
      • 1970-01-01
      • 2014-12-01
      • 1970-01-01
      • 2014-05-21
      • 1970-01-01
      • 1970-01-01
      • 2014-01-05
      相关资源
      最近更新 更多