何时在Python中使用弱引用?

时间:2010-03-12 22:25:20

标签: python weak-references

任何人都可以解释弱引用的用法吗?

documentation并没有准确地解释它,它只是说GC可以随时通过弱引用破坏链接到的对象。那么一个物体可以随时消失的重点是什么?如果我需要在它消失后立即使用该怎么办?

你能用一些好的例子解释一下吗?

由于

3 个答案:

答案 0 :(得分:34)

事件是弱引用的常见场景。


问题

考虑一对物体:发射器和接收器。接收器的寿命比发射器短。

您可以尝试这样的实现:

class Emitter(object):

    def __init__(self):
        self.listeners = set()

    def emit(self):
        for listener in self.listeners:
            # Notify
            listener('hello')


class Receiver(object):

    def __init__(self, emitter):

        emitter.listeners.add(self.callback)

    def callback(self, msg):
        print 'Message received:', msg


e = Emitter()
l = Receiver(e)
e.emit() # Message received: hello

但是,在这种情况下,Emitter会保留对绑定方法callback的引用,该方法保留对Receiver的引用。所以发射器让接收器保持活力:

# ...continued...

del l
e.emit() # Message received: hello

这有时很麻烦。想象一下,Emitter是某些数据模型的一部分,它指示数据何时发生变化,而Receiver是由一个对话框窗口创建的,该对话框窗口会监听该更改以更新某些UI控件。

通过应用程序的生命周期,可以生成多个对话框,并且我们不希望它们的接收器在窗口关闭后很长时间内仍然在Emitter中注册。这将是一次内存泄漏。

手动删除回调是一种选择(同样麻烦),使用弱引用是另一种选择。


解决方案

有一个很好的类WeakSet看起来像普通集,但是使用弱引用存储它的成员,并且在它们被释放时不再存储它们。

出色!让我们用它:

def __init__(self):
    self.listeners = weakref.WeakSet()

然后再跑:

e = Emitter()
l = Receiver(e)
e.emit()
del l
e.emit()

哦,根本没有任何事情发生!那是因为绑定方法(特定接收者的callback)现在是孤立的 - 发射器和接收器都没有强烈的引用。因此,它立即收集垃圾。

让我们让接收者(这次不是发射者)对这个回调保持强烈的引用:

class Receiver(object):

    def __init__(self, emitter):

        # Create the bound method object
        cb = self.callback

        # Register it
        emitter.listeners.add(cb)
        # But also create an own strong reference to keep it alive
        self._callbacks = set([cb])

现在我们可以观察到预期的行为:只要接收器存在,发射器就只保留回调。

e = Emitter()
l = Receiver(e)
assert len(e.listeners) == 1

del l
import gc; gc.collect()
assert len(e.listeners) == 0

引擎盖下

请注意,我必须在此放置gc.collect()以确保接收器立即被清理干净。这里需要它,因为现在有一个强引用循环:绑定方法指向接收器,反之亦然。

这不是很糟糕;这只意味着接收器的清理将推迟到下一个垃圾收集器运行。简单的引用计数机制无法清除循环引用。

如果你真的想要,你可以通过用自定义函数对象替换绑定方法来删除强引用循环,自定义函数对象也将其self作为弱引用。

def __init__(self, emitter):

    # Create the bound method object
    weakself = weakref.ref(self)
    def cb(msg):
        self = weakself()
        self.callback(msg)

    # Register it
    emitter.listeners.add(cb)
    # But also create an own strong reference to keep it alive
    self._callbacks = set([cb])

让我们把这个逻辑放到辅助函数中:

def weak_bind(instancemethod):

    weakref_self = weakref.ref(instancemethod.im_self)
    func = instancemethod.im_func

    def callback(*args, **kwargs):
        self = weakref_self()
        bound = func.__get__(self)
        return bound(*args, **kwargs)

    return callback

class Receiver(object):

    def __init__(self, emitter):

        cb = weak_bind(self.callback)

        # Register it
        emitter.listeners.add(cb)
        # But also create an own strong reference to keep it alive
        self._callbacks = set([cb])

现在没有强引用循环,所以当释放Receiver时,回调函数也会立即被释放(并从发射器的WeakSet中删除),而不需要完整的GC循环

答案 1 :(得分:25)

弱引用的典型用法是如果A有对B的引用而B有对A的引用。没有适当的循环检测垃圾收集器,即使没有引用,这两个对象也永远不会得到GC。要么来自“外部”。但是,如果其中一个引用是“弱”,则对象将正确地进行GC。

然而,Python 确实有一个循环检测垃圾收集器(自2.0以来!),因此不计算:)

弱引用的另一个用途是缓存。它在weakref文档中提到:

  

弱引用的主要用途是实现容纳大对象的缓存或映射,其中希望大对象不能仅仅因为它出现在缓存或映射中而保持活动。

如果GC决定销毁其中一个对象,并且您需要它,则可以重新计算/重新获取数据。

答案 2 :(得分:2)

  • 弱引用是python中的一个重要概念,缺少 在语言中喜欢Java(java 1.5)。
  • 在Observer设计模式中,通常Observable Object必须维护 对Observer对象的弱引用。

    例如。 A发出一个事件done()和B向A注册,它想要 听完事件()。因此,每当发出done()时,B就是 收到通知。但如果在申请中不需要B,那么A一定不能 成为A中垃圾收集的一个障碍(因为A持有 参考B)。因此,如果A对B具有弱引用,则何时 所有对A的引用都没有了,那么B将被垃圾收集。

  • 它在实现缓存方面也非常有用。