【问题标题】:Problems in passing numpy.ndarray to ctypes but to get an erraneous result将 numpy.array 传递给 ctypes 但得到错误结果的问题
【发布时间】:2014-03-14 08:46:43
【问题描述】:

注意:这不是一个问题 - 我解决了它并将其发布在这里,试图分享我学到的东西。

我昨晚在使用 numpy 时遇到了一个问题,下面是我如何将其简化为短代码。起初它对我来说似乎是一个错误,但是当我尝试编写这个问题时,我意识到这是我自己的错误。希望以后也遇到这个问题的其他人可以从中受益!

这可以在我的 Win 7 x64 上使用 WinSDK 7.1 的 C 编译器重复。 Python 版本为 3.3.3,使用 MSC v.1600 构建。 Numpy 版本是 1.8.0。

0) 简要总结:当我将 ndarray 传递给从 c 代码编译的 dll 时,c 代码看到的数组与我传入的数组不同。

1) 写一段c代码:

// testdll.c
#include <stdlib.h>

__declspec(dllexport) void copy_ndarray(double *array1, double *array2, size_t array_length);

void copy_ndarray(double *array1, double *array2, size_t array_length)
{
    size_t i;
    for(i=0; i<array_length; i++)
        array2[i] = array1[i];
    return;
}

2) 编写python代码:

import numpy as np
import ctypes


# wrap the function from dll to python
lib = ctypes.cdll.LoadLibrary('./testdll.dll')
fun = lib.copy_ndarray
fun.restype = None
fun.argtypes = [np.ctypeslib.ndpointer(ctypes.c_double), np.ctypeslib.ndpointer(ctypes.c_double), ctypes.c_size_t]
# Initialize array1 and array2
array_length= 10
temp = np.c_[100.*np.ones(array_length), 200.*np.ones(array_length)]
array1 = temp[:, 1]
array2 = np.zeros(array_length)
fun(array1, array2, array_length)

3) 运行代码。看看 array1 和 array2 有什么不同。

【问题讨论】:

  • IMO,您应该将标题更改为看起来像实际问题的内容或使其对 Google 搜索友好,以便将来的用户可以轻松找到它。 :)
  • @AshwiniChaudhary 谢谢!我同意。我已经更新了,欢迎大家多提建议!另外,我将其标记为社区 wiki 可以意识到无法撤消...您认为我还应该让它打开以查看其他人是否有更好/更整洁的方法吗? :)
  • 其他人仍然可以发布答案,所以这不是问题。
  • @AshwiniChaudhary 哦,我明白了……抱歉,我误解了社区 wiki 的概念。感谢您的澄清!

标签: python c numpy ctypes


【解决方案1】:

当然应该不一样!

当我使用array1 = temp[:, 1] 时,array1 不是真正大小的 (10,) ndarray。它是temp 的视图,大小为(10, 2)。想想它是如何存储在内存中的——当指针在c中指向另一个sizeof(double)时,它会遇到temp中的下一个元素,而不是array1中的。

修复它的方法是 - 在读取数据时不要使用 ndarray 视图!使用这条线

array1 = temp[:, 1].copy()

制作副本,而不是简单地使用视图。

正确的python代码是:

import numpy as np
import ctypes


# wrap the function from dll to python
lib = ctypes.cdll.LoadLibrary('./testdll.dll')
fun = lib.copy_ndarray
fun.restype = None
fun.argtypes = [np.ctypeslib.ndpointer(ctypes.c_double), np.ctypeslib.ndpointer(ctypes.c_double), ctypes.c_size_t]
# Initialize array1 and array2
array_length= 10
temp = np.c_[100.*np.ones(array_length), 200.*np.ones(array_length)]
array1 = temp[:, 1].copy()
array2 = np.zeros(array_length)
fun(array1, array2, array_length)

我个人觉得这很棘手,因为作为一名数据分析员(老实说,我不是...我是一名研究人员,但已经足够接近了!),99% 的情况下,视图比副本更好,因为它更快,而且一旦数据被读取,我们就不需要原始的ndarray。

学习这一点并牢记这一点很好!

【讨论】:

  • 您也可以通过np.frombuffer(temp[:, 1]) 看到这一点。这会在视图上调用 PyObject_AsWriteBuffer,它在 3.x 中。获取对象的Py_buffer 并返回buf 指针和len。如果它改为使用PyObject_GetBuffer,它可以根据PyBuffer.strides重构视图。
  • @eryksun 感谢您基于 CPython 实现的贡献!这真的很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-07
相关资源
最近更新 更多