如何为C分配的numpy数组注册析构函数?

dsi*_*ign 7 c python numpy

我想在C/C++中为numpy数组分配数字,并将它们作为numpy数组传递给python.我可以使用PyArray_SimpleNewFromData.

问题是我还想注册一个当numpy数组引用计数器达到零时应该从Python调用的函数,这将调用C端的一些析构函数语义...这是我需要的一个伪示例:

 float* arr; PyObject* np_arr; void (*destructor)(float* arr);
 // ... C-allocate array on arr, ...
 // ...
 // ... initialize destructor with some suitable value, and then:
 np_arr = /* ... create the array to wrap arr, 
             and to use destructor on some meaningful way ... */
Run Code Online (Sandbox Code Playgroud)

有一个简单的方法吗?

Arn*_*yal 7

这个想法是创建一个Python对象,该对象知道在销毁时如何释放内存,并将其作为返回的C分配的numpy数组的基础。这听起来很棘手,但可以通过python中的胶囊轻松实现。让我举个例子,

假设您有以下代码,

PyObject *arr;
int nd = 2;
npy_intp dims[] = {5, 10};
double *data = some_function_that_returns_a_double_star(x, y, z);

arr = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, (void *)data);
return arr;
Run Code Online (Sandbox Code Playgroud)

这里有一个明显的内存泄漏,因为你不能到ARR免费的数据是因为它说删除这里的红色警告框。另一方面,修复此问题很容易。定义一个基本上是知道如何进行垃圾回收的析构函数的函数。

void capsule_cleanup(PyObject *capsule) {
    void *memory = PyCapsule_GetPointer(capsule, NULL);
    // Use your specific gc implementation in place of free if you have to
    free(memory);
}
Run Code Online (Sandbox Code Playgroud)

现在,将代码扩展为

PyObject *arr;
int nd = 2;
npy_intp dims[] = {5, 10};
double *data = some_function_that_returns_a_double_star(x, y, z);

arr = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, (void *)data);
PyObject *capsule = PyCapsule_New(data, NULL, capsule_cleanup);
// NULL can be a string but use the same string while calling PyCapsule_GetPointer inside capsule_cleanup
PyArray_SetBaseObject((PyArrayObject *) arr, capsule);
return arr;
Run Code Online (Sandbox Code Playgroud)

无需Py_DECREF胶囊。该函数PyArray_SetBaseObject窃取参考。

希望这可以帮助!