需要深入挖掘C代码才能说明这一点,但是:
行
view = <double[:N]> ptr
实际上生成了一个
__pyx_array_obj
。这与
文档中详细介绍的"Cython array"和可导入为cython.view.array
的类型相同。Cython数组确实有一个可选成员称为
callback_free_data
,可以充当析构函数。
该行的翻译如下:
struct __pyx_array_obj *__pyx_t_1 = NULL;
__pyx_t_1 = __pyx_array_new(__pyx_t_2, sizeof(double), PyBytes_AS_STRING(__pyx_t_3), (char *) "c", (char *) __pyx_v_ptr);
__pyx_t_2和__pyx_t_3只是暂存大小和格式的临时变量。如果我们看一下__pyx_array_new的内部,首先可以看到数组的data成员直接赋值为传递的__pyx_v_ptr值。
__pyx_v_result->data = __pyx_v_buf;
即不会产生副本,并且没有设置 `callback_free_data`。顺便提一下:`cython.view.array` 的 C 代码实际上是从
Cython code 自动生成的,所以如果您想进一步研究,阅读生成的 C 代码可能比较容易。
基本上,memoryview 包含一个指向原始数据的 cython.view.array 指针,但没有设置 callback_free_data。当 memoryview 销毁时,会调用 cython.view.array 的析构函数。这会清除一些内部内容,但不会释放它所指向的数据(因为它没有释放数据的指示)。
因此,在调用 PyMem_Free 后访问 memoryview 是不安全的。你似乎能够成功访问是运气好而已。只要不访问它,memoryview 继续存在是安全的。例如下面的函数:
def good():
cdef double* ptr
cdef double[::1] view
ptr = <double*> PyMem_Malloc(N*sizeof('double'))
try:
view = <double[:N]> ptr
finally:
PyMem_Free(ptr)
会很好。一个类似于以下的功能:
def bad():
cdef double* ptr
cdef double[::1] view
ptr = <double*> PyMem_Malloc(N*sizeof('double'))
try:
view = <double[:N]> ptr
finally:
PyMem_Free(ptr)
view[0] = 0
return view
这将是一个不好的主意,因为它返回了一个不指向任何东西的内存视图,并在查看的数据被释放后访问
view
。
你一定要确保在某个时候调用
PyMem_Free
,否则会有内存泄漏。如果
view
被传递并且其生命周期难以跟踪,一种方法是手动创建一个设置了
callback_free_data
的
cython.view.array
:
cdef view.array my_array = view.array((N,), allocate_buffer=False)
my_array.data = <char *> ptr
my_array.callback_free_data = PyMem_Free
view = my_array
如果
view
的生命周期是明显的,那么您可以像以前一样在
ptr
上调用
PyMem_Free
。