我能强迫一个numpy ndarray接管它的内存吗?

2024-04-27 04:11:11 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个C函数,它mallocs()并填充一个二维的浮点数组。它“返回”这个地址和数组的大小。签名是

int get_array_c(float** addr, int* nrows, int* ncols);

我想从Python调用它,所以我使用ctypes。在

^{pr2}$

我从没想过如何用ctypes指定参数类型。我倾向于为我使用的每个C函数编写一个python包装器,并确保包装器中的类型正确无误。floats数组是一个按列主要顺序排列的矩阵,我想把它作为努比·恩达雷. 但是它相当大,所以我想使用C函数分配的内存,而不是复制它。(我刚刚在这个StackOverflow答案中找到了PyBuffer_FromMemory的东西:https://stackoverflow.com/a/4355701/3691

buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object

import numpy
def get_array_py():
    nrows = ctypes.c_int()
    ncols = ctypes.c_int()
    addr_ptr = ctypes.POINTER(ctypes.c_float)()
    get_array_c(ctypes.byref(addr_ptr), ctypes.byref(nrows), ctypes.byref(ncols))
    buf = buffer_from_memory(addr_ptr, 4 * nrows * ncols)
    return numpy.ndarray((nrows, ncols), dtype=numpy.float32, order='F',
                         buffer=buf)

这似乎给了我一个具有正确值的数组。但我敢肯定是内存泄漏。在

>>> a = get_array_py()
>>> a.flags.owndata
False

数组不拥有内存。很公平;在默认情况下,当数组从缓冲区创建时,它不应该这样做,但在本例中应该这样做。当numpy数组被删除时,我真的希望python为我释放缓冲内存。似乎如果我可以强制owndata为True,那么应该可以这样做,但是owndata是不可设置的。在

不满意的解决方案:

  1. 使调用方释放数组的内存。这太烦人了;调用者应该能够像对待其他numpy数组一样对待这个numpy数组。

  2. 在get_array_py中将原始数组复制到一个新的numpy数组中(有它自己的独立内存),删除第一个数组,然后释放get_array_py()中的内存。返回副本而不是原始数组。这很烦人,因为它应该是一个不必要的内存拷贝。

有办法做我想做的吗?我不能修改C函数本身,尽管我可以在库中添加另一个C函数,如果有帮助的话。在


Tags: 函数内存frompynumpygetbuffer数组
2条回答

我刚刚偶然发现了这个问题,在2013年8月,这个问题仍然是个问题。Numpy对OWNDATA标志非常挑剔:它不可能在Python级别上修改,因此ctypes很可能无法做到这一点。在numpy C-API级别上(现在我们讨论的是一种完全不同的制作Python扩展模块的方法),必须使用以下内容显式设置标志:

PyArray_ENABLEFLAGS(arr, NPY_ARRAY_OWNDATA);

在numpy<;1.7版中,人们必须更加明确:

^{pr2}$

如果可以控制底层的C函数/库,最好的解决方案是从Python向它传递一个适当大小的空numpy数组来存储结果。基本原则是内存分配应该总是在尽可能高的级别上进行,在本例中是在Python解释器级别上进行的。在


正如kynan在下面评论的,如果您使用Cython,您必须手动公开PyArray_ENABLEFLAGS函数,请参阅本文Force NumPy ndarray to take ownership of its memory in Cython。在

相关文档是herehere。在

我倾向于从C库中导出两个函数:

int get_array_c_nomalloc(float* addr, int nrows, int ncols); /* Pass addr as argument */
int get_array_c(float **addr, int nrows, int ncols); /* Calls function above */

然后,我将编写get_array_c的Python包装器[1]来分配数组,然后调用get_array_c_nomalloc。然后Python拥有内存。您可以将这个包装器集成到库中,这样您的用户就不必知道get_array_c_nomalloc的存在。在

[1]这不再是真正的包装器,而是一个适配器。在

相关问题 更多 >