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

时间:2015-11-02 12:52:42

标签: python c 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)

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

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

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

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

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

清除 NumPy阵列不占用C阵列内存的所有权而且我不想要它。我想要MultimediaParams来负责释放C结构(包含数组数据),但只要NumPy对象存活就不要这样做。

有什么建议吗?

1 个答案:

答案 0 :(得分:2)

正如@ JFSebastian的评论所指出的那样,问题很可能是当您正确地将指向MultimediaParams实例的指针指向NumPy数组的base引用时,实际上增加了它的引用计数,因为赋值是用C语言编写的,而不是用Python语言编写的。这可能导致MultimediaParams对象的过早垃圾收集,其内存被重用并导致您在ndarray中作为垃圾数据体验。

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