将C数组绑定到Numpy数组而不进行复制

Max*_*gal 11 c python arrays numpy cython

我正在编写一个Python类,它将包含一个包含C结构的C模块.我使用的是Cython语言(Python和C的超集语言).C结构在构造函数中是malloc,并包含一个我想在Python中使用的数组.该数组将在Python中表示为NumPy数组,但我不想将值复制到它.我想将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)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,该类具有其__dealloc__方法,该方法将处理在C中分配的内存,并在没有对MultimediaParams实例的引用时释放它.

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

问题:当MultimediaParams释放对象并释放数组的内存时,NumPy对象仍然指向刚刚释放的内存.当NumPy对象尝试访问/修改已释放的内存时,这将导致段错误.

MultimediaParams只要有NumPy对象使用其内存,我怎样才能确保对象没有被释放?

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

澄清:NumPy数组不占用C数组内存,我不希望它.我想MultimediaParams负责释放C结构(包含数组数据),但只要NumPy对象存活就不要这样做.

有什么建议?

Hen*_*rik 2

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

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