转换为 numpy 数组时结构子类列表 returns 错误值

List of Structure subclass returns wrong values when casting to numpy array

我构建了一个简单的 Structure 子类,它有两个字段,包含一个指向数组的空指针和数组长度。但是,当我尝试使用相同长度的输入列表创建这些列表时,返回的 void 指针的值与用于创建实例的最后一个数组相同:

from ctypes import POINTER, c_double, c_size_t, c_void_p, Structure, cast
import numpy as np

class External(Structure):

    _fields_ = [("data", c_void_p),
                ("length", c_size_t)]
    @classmethod
    def from_param(cls, seq):
        return seq if isinstance(seq, cls) else cls(seq)

    def __init__(self, seq):
        self.ptr = cast(
            np.array(seq, dtype=np.float64).ctypes.data_as(POINTER(c_double)),
            c_void_p
        )
        self.data = self.ptr
        self.length = len(seq)
        # recreate array from void pointer
        # shows the correct values
        shape = self.length, 2
        ptr = cast(self.data, POINTER(c_double))
        array = np.ctypeslib.as_array(ptr, shape)
        print "Correct array", array.tolist()

if __name__ == "__main__":
    interiors = [
            [[3.5, 3.5], [4.4, 2.0], [2.6, 2.0], [3.5, 3.5]],
            [[4.0, 3.0], [4.0, 3.2], [4.5, 3.2], [4.0, 3.0]],
            ]
    wrong = [External(s) for s in interiors]
    for w in wrong:
        # perform same cast back to array as before
        shape = w.length, 2
        ptr = cast(w.data, POINTER(c_double))
        array = np.ctypeslib.as_array(ptr, shape)
        print "Wrong array", array.tolist()

如果我使用不同长度的输入列表创建我的 External 实例,一切都会按预期进行。我在这里做错了什么?

问题是 numpy 数组会立即被垃圾回收并释放底层内存,从而导致悬空指针。

解决方案是保留对底层 buffer 对象的引用:

def __init__(self, seq):
    array = np.array(seq, dtype=np.float64)
    self._buffer = array.data
    self.ptr = cast(
        array.ctypes.data_as(POINTER(c_double)),
        c_void_p
    )
    ...

现在只有当 External 持有引用的实例被删除时,数组的内存才会被释放。