在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
时死亡?
答案 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
类想要自行删除时,请循环fakeref
,None
对Foo
的引用。瞧;您的班级可以根据需要“删除”自己,所有fakeref
现在都会返回None
。但就像使用weakref
一样,存储调用结果会使其再次成为强引用,并且您的类将无法以您希望的方式删除自己。
所有这一切都说,我认为你没有提供足够好的理由说明为什么这是必要的。你所说的只是“它没有理由留在记忆中”。嗯,有:它需要在那里引用它的对象。如果在某个时间点它变得无用,那么你的对象不应该持有对它的引用。当引用它的对象不再关心它时,它们应该删除那些引用。然后Python将对其进行清理,而无需您进一步干预。
1 如果您不想依赖弱引用,fakeref
可以实施__del__
并从您拥有的Foo
实例中删除自己引用(如果不是None
)。