3
import weakref
import gc

class MyClass(object):
    def refer_to(self, thing):
        self.refers_to = thing

foo = MyClass()
bar = MyClass()
foo.refer_to(bar)
bar.refer_to(foo)
foo_ref = weakref.ref(foo)
bar_ref = weakref.ref(bar)
del foo
del bar
gc.collect()
print foo_ref()

我希望foo_refbar_ref保留对 和 的弱引用,foo只要bar它们相互引用*,但这会打印None. 如何防止垃圾收集器在引用周期内收集某些对象?

bar应该在此代码中进行垃圾收集,因为它不再是foo-bar引用循环的一部分:

baz = MyClass()
baz.refer_to(foo)
foo.refer_to(baz)
gc.collect()

*我意识到阻止循环引用被垃圾收集似乎毫无意义,但我的用例需要它。我有一堆对象,它们以类似 web 的方式相互引用,还有WeakValueDictionary一个对束中的每个对象保持弱引用的对象。我只希望在该组中的一个对象被孤立时进行垃圾收集,即当该组中没有其他对象引用它时。

4

1 回答 1

2

通常使用弱引用意味着您无法阻止对象被垃圾收集。

但是,您可以使用一个技巧来防止引用循环中的对象被垃圾收集:在这些对象上定义一个__del__()方法

gc模块文档

gc.垃圾

收集器发现无法访问但无法释放的对象列表(无法收集的对象)。默认情况下,此列表仅包含具有__del__()方法的对象。具有 __del__()方法并且是引用循环一部分的对象会导致整个引用循环无法收集,包括不一定在循环中但只能从循环中访问的对象。Python 不会自动收集此类循环,因为通常 Python 无法猜测运行循环的安全顺序__del__()方法。如果您知道安全顺序,则可以通过检查垃圾列表来强制执行该问题,并由于列表中的对象而显式中断循环。请注意,由于位于垃圾列表中,这些对象仍然保持活动状态,因此它们也应该从垃圾中删除。例如,在中断循环后,del gc.garbage[:]清空列表。通常最好通过不创建包含具有__del__()方法的对象的循环来避免该问题,并且在这种情况下可以检查垃圾以验证没有创建此类循环。

当您定义MyClass如下:

class MyClass(object):
    def refer_to(self, thing):
        self.refers_to = thing
    def __del__(self):
        print 'Being deleted now, bye-bye!'

然后您的示例脚本打印:

<__main__.MyClass object at 0x108476a50>

但注释掉其中一个.refer_to()调用会导致:

Being deleted now, bye-bye!
Being deleted now, bye-bye!
None

换句话说,通过简单地定义一个__del__()方法,我们防止了引用循环被垃圾收集,但是任何孤立的对象被删除了。

请注意,为了使它起作用,您需要循环引用;无论如何,对象图中属于参考圆的任何对象都将被剔除。

于 2012-08-19T12:11:20.360 回答