在不复制的情况下将 C 数组绑定到 Numpy 数组

Binding C array to Numpy array without copying

我正在编写一个 Python class,它将包装一个包含 C 结构的 C 模块。我使用的是 Cython 语言(Python 和 C 的超集语言)。 C 结构在构造函数中被 malloc,并包含一个我想在 Python 中使用的数组。该数组将在 Python 中表示为 NumPy 数组,但我不想将值复制到它。我想 link NumPy 数组直接到 malloc 内存。对于此任务,我使用 NumPy Array API,特别是此函数:

PyObject*PyArray_SimpleNewFromData(int nd, npy_intp* dims, int typenum, void* data)

我设法在 Cython 中使用这段代码将 NumPy 数组绑定到 C 结构的数组,只要 NumPy 数组和 MultimediaParams 对象具有相同的生命周期,它就可以正常工作:

cdef class MultimediaParams:
    def __init__(self, **kwargs):
        self._mm_np = < mm_np *> malloc(sizeof(mm_np))
        #some code...

    def as_ndarray(self): #TODO: what if self deallocated but numpy array still exists(segfault?)
        cdef numpy.npy_intp shape[1]
        cdef int arr_size = sizeof(self._mm_np[0].n2) / sizeof(self._mm_np[0].n2[0])
        shape[0] = < numpy.npy_intp > arr_size
        cdef numpy.ndarray ndarray
        ndarray = numpy.PyArray_SimpleNewFromData(1, shape, numpy.NPY_DOUBLE, self._mm_np[0].n2)

        return ndarray

    def __dealloc__(self):
        free(self._mm_np)

如您所见,class 有其 __dealloc__ 方法,它将处理 C 中分配的内存,并在没有对 MultimediaParams 实例的引用时释放它。

在这种绑定中,NumPy 不拥有数组的内存。

问题:当MultimediaParams对象被释放,数组的内存被释放时,NumPy对象仍然指向刚刚被释放的内存。当 NumPy 对象尝试 access/modify 已释放的内存时,这将导致段错误。

只要有一个 NumPy 对象在使用它的内存,我怎样才能确保 MultimediaParams 对象不被释放?

据我所知,我需要做的就是让 NumPy 对象引用一个 MultimediaParams 实例,它从中获得要指向的内存。 我尝试使用 ndarray.base = <PyObject*>self 这样 NumPy 就会知道它的基础对象,这应该会添加另一个对 MultimediaParams 实例的引用,并且只要 NumPy 数组还活着就会导致它不会被释放。这一行导致我的测试失败,因为 NumPy 数组的内容变成了垃圾。

澄清NumPy 数组不拥有 C 数组内存,我不希望它拥有。我想要MultimediaParams 负责释放 C 结构(包含数组数据),但只要 NumPy 对象还活着就不会这样做。

有什么建议吗?

正如@J.F.Sebastian 的评论所指出的,问题很可能是当您正确地将指向 MultimediaParams 实例的指针分配给 NumPy 数组的 base 引用时,您实际上并没有增加它的引用计数,因为赋值是在 C 中进行的,而不是在 Python 中进行的。这可能会导致 MultimediaParams 对象的过早垃圾收集,其内存被重用并导致您在 ndarray 中遇到垃圾数据。

使用宏 Py_INCREF 手动增加 MultimediaParams 对象的引用计数应该会产生所需的行为。