返回 numpy 数组别名内部数组时如何处理引用计数?

How to handle reference counting when returning numpy array aliasing internal array?

我有一个名为 ctcorrgen 的 class 正在执行一些数字处理,并且 return 通过指向内部数组的 const 指针一次得到一行结果。我想将这个内部数组包装成一个只读的 Numpy 数组并 return 它,像这样:

static inline PyObject* ctcorrgen_yield_row(object &object) {
    // extract corrgen base
    ctcorrgen &corrgen = extract<ctcorrgen&>(object);

    // get row of data
    const cfloat* row = corrgen.yield_row();
    if (row == nullptr) {
        return detail::none();        
    } else {
        // build read-only array around data
        npy_intp len = corrgen.framesize();
        return PyArray_New(
            &PyArray_Type, 1, &len, NPY_COMPLEX64, NULL, (void*)row, 0,
            NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_ALIGNED, NULL
        );
    }
}

我的问题是,我如何设置才能使新数组对象引用创建它的对象,这样它就不会在我们完成数组之前被垃圾收集(从而破坏底层缓冲区)?我正在使用 boost::python,但我怀疑这需要比我更直接地了解 Python C-API。

好的,我想我找到了答案,对于任何追随我的人。

查看 PyArrayObject 的定义:

typedef struct PyArrayObject {
    PyObject_HEAD
    char *data;
    int nd;
    npy_intp *dimensions;
    npy_intp *strides;
    PyObject *base;
    PyArray_Descr *descr;
    int flags;
    PyObject *weakreflist;
} PyArrayObject;

关键是base pointer:

PyObject *PyArrayObject.base

This member is used to hold a pointer to another Python object that is related to this array. There are two use cases: 1) If this array does not own its own memory, then base points to the Python object that owns it (perhaps another array object), 2) If this array has the (deprecated) NPY_ARRAY_UPDATEIFCOPY or :c:data:NPY_ARRAY_WRITEBACKIFCOPY`: flag set, then this array is a working copy of a “misbehaved” array. When PyArray_ResolveWritebackIfCopy is called, the array pointed to by base will be updated with the contents of this array.

结合PyArray_New的这段:

Warning

If data is passed to PyArray_NewFromDescr or PyArray_New, this memory must not be deallocated until the new array is deleted. If this data came from another Python object, this can be accomplished using Py_INCREF on that object and setting the base member of the new array to point to that object. If strides are passed in they must be consistent with the dimensions, the itemsize, and the data of the array.

所以,我相信这样的事情是建立所需关系的合适方法(对于 Numpy >= 1.7)

// increment reference to ctcorrgen object and set base pointer
// of array, this will establish an ownership link so that
// ctcorrgen won't be destroyed before the array.
incref(object.ptr());
PyArray_SetBaseObject((PyArrayObject*)array, object.ptr());