如何让这个容器自行删除?

时间:2014-06-07 03:05:10

标签: python

this问题上捎带,说我有一个弱容器的容器:

import weakref

class Foo(object):
    a = lambda *_: None

    def __init__(self, a):
        self.a = weakref.ref(a, self._remove)

    def _remove(self, *args):
        self.__del__(self)


class Bar(object):
    pass


>>> bar = Bar()
>>> foo = Foo(bar)
>>> del bar
>>> foo
<__main__.Foo object at 0x...>

我考虑将Foo实例存储在静态WeakKeyDictionary容器中,并将a属性作为键,并在任何地方使用weakref.proxy实例 - 但这似乎......效率低下。最好的方法是什么,以便Foo实例在引用a时死亡?

1 个答案:

答案 0 :(得分:2)

你做不到。我花了一些时间挖掘Python源代码和ctypes文档来讽刺地展示在我放弃之前,人们如何真正删除(又称Py_DECREF直到解除分配)对象。关键是,你真的不想这样做。 Python管理自己的内存是有原因的。当然,它可以让你访问弱引用之类的内容,但在任何情况下都不会破坏强大的引用。

您提出的建议是让对象进入加载到Python解释器中的每一段代码的环境中,以消除对自身的任何引用。 weakref也必须删除引用,但它只需要从weakref对象中删除引用;它不必触摸持有weakref引用的对象。以你提出的方式删除引用至少是侵入性的,很可能是不可能的。

要了解为什么不可能,请考虑如何在C中编写定义类型的Python模块。对象的每个实例都会保存一些PyObject指针,指向它关心的事物。其中一些可能通过属性暴露给Python,而其他可能仍然是内部的。假设其中一个内部引用引用了您的Foo个对象之一。为了“删除”它本身,它必须进入我们的C类型和NULL引用。但是对于Python代码,定义对象的C结构是不透明的。如果你用ctypes挖掘它,你可以检查字节,但是谁知道某些字节序列是指向你的对象的指针还是int恰好具有与你的对象的地址?你不能,至少在不知道该类型的实现细节的情况下。并且你无法处理每一个案例,因为有人可以通过导入另一个用C编写的模块来添加另一个案例。你无法预料到一切。

那你能做什么?如果你对做这样的事情感到厌烦,你可以模仿weakref的界面。基本上,创建一个新类,其中包含对您的类的引用;为了避免含糊不清,我将其称为fakeref。当它被调用时,它返回你的类的实例。你的类对所有fakeref都有弱引用 1 。每当您的Foo类想要自行删除时,请循环fakerefNoneFoo的引用。瞧;您的班级可以根据需要“删除”自己,所有fakeref现在都会返回None。但就像使用weakref一样,存储调用结果会使其再次成为强引用,并且您的类将无法以您希望的方式删除自己。

所有这一切都说,我认为你没有提供足够好的理由说明为什么这是必要的。你所说的只是“它没有理由留在记忆中”。嗯,有:它需要在那里引用它的对象。如果在某个时间点它变得无用,那么你的对象不应该持有对它的引用。当引用它的对象不再关心它时,它们应该删除那些引用。然后Python将对其进行清理,而无需您进一步干预。


1 如果您不想依赖弱引用,fakeref可以实施__del__并从您拥有的Foo实例中删除自己引用(如果不是None)。