什么时候在Python中使用弱引用?

2024-06-29

谁能解释一下弱引用的用法吗?

The 文档 http://docs.python.org/library/weakref.html没有具体解释,只是说GC可以随时销毁通过弱引用链接到的对象。那么拥有一个随时可能消失的物体还有什么意义呢?如果我需要在它消失后立即使用它怎么办?

您能用一些好的例子来解释它们吗?

Thanks


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


Problem

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

你可以尝试这样的实现:

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

然而,在这种情况下,发射器保留对绑定方法的引用callback保留对接收者的引用。因此发射器使接收器保持活动状态:

# ...continued...

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

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

在应用程序的生命周期中,可以生成多个对话框,并且我们不希望在窗口关闭很久之后它们的接收器仍然在发射器内注册。那将是内存泄漏。

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


Solution

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

出色的!让我们使用它:

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

并再次运行:

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

噢,什么也没发生!这是因为绑定方法(特定接收者的callback) 现在是孤立的 - 发射器和接收器都没有对它有强引用。因此它会立即被垃圾收集。

让我们让 Receiver(这次不是 Emitter)保留对此回调的强引用:

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 周期。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么时候在Python中使用弱引用? 的相关文章

随机推荐