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

发布于 2024-08-24 22:40:35 字数 216 浏览 7 评论 0原文

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

文档没有准确解释它,它只是说GC可以销毁对象随时通过弱引用链接。那么拥有一个随时可能消失的物体还有什么意义呢?如果我需要在它消失后立即使用它怎么办?

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

谢谢

Can anyone explain usage of weak references?

The documentation doesn't explain it precisely, it just says that the GC can destroy the object linked to via a weak reference at any time. Then what's the point of having an object that can disappear at any time? What if I need to use it right after it disappeared?

Can you please explain them with some good examples?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

悟红尘 2024-08-31 22:40:35

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


问题

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

您可以尝试这样的实现:

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

但是,在这种情况下,发射器保留对绑定方法回调的引用,该方法保留对接收器的引用。所以发射器让接收器保持活动状态:

# ...continued...

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

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

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

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


解决方案

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

出色的!让我们使用它:

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

然后再次运行:

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

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

让我们让 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])

现在我们可以观察预期的行为:Emitter 仅在 Receiver 存在期间保留回调。

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 被释放时,回调函数也将被释放(并从 Emitter 的 WeakSet 中删除) >) 立即执行,无需完整的 GC 周期。

Events are a common scenario for weak references.


Problem

Consider a pair of objects: Emitter and Receiver. The receiver has shorter lifetime than the emitter.

You could try an implementation like this:

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

However, in this case, the Emitter keeps a reference to a bound method callback that keeps a reference to the Receiver. So the Emitter keeps the Receiver alive:

# ...continued...

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

This is sometimes troublesome. Imagine that Emitter is a part of some data model that indicates when data changes and Receiver was created by a dialog window that listens to that changes to update some UI controls.

Through the application's lifetime, multiple dialogs can be spawned and we don't want their receivers to be still registered inside the Emitter long after the window had been closed. That would be a memory leak.

Removing the callbacks manually is one option (just as troublesome), using weak references is another.


Solution

There's a nice class WeakSet that looks like a normal set but stores its members using weak references and no longer stores them when they are freed.

Excellent! Let's use it:

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

and run again:

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

Oh, nothing happens at all! That's because the bound method (a specific receiver's callback) is orphaned now - neither the Emitter nor the Receiver hold a strong reference to it. Hence it's garbage collected immediately.

Let's make the Receiver (not the Emitter this time) keep a strong reference to this 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])

Now we can observe the expected behaviour: the Emitter only keeps the callback as long as the Receiver lives.

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

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

Under the hood

Note that I've had to put a gc.collect() here to make sure that the receiver is really cleaned up immediately. It's needed here because now there's a cycle of strong references: the bound method refers to the receiver and vice versa.

This isn't very bad; this only means that the receiver's cleanup will be deferred until the next garbage collector run. Cyclic references can't be cleaned up by the simple reference counting mechanism.

If you really want, you could remove the strong reference cycle by replacing the bound method with a custom function object that would keep its self as a weak reference too.

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])

Let's put that logic into a helper function:

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])

Now there's no cycle of strong references, so when Receiver is freed, the callback function will also be freed (and removed from the Emitter's WeakSet) immediately, without the need for a full GC cycle.

小矜持 2024-08-31 22:40:35

弱引用的典型用途是如果 A 引用了 B 并且 B 引用了 A。如果没有适当的循环检测垃圾收集器,即使没有来自 A 的引用,这两个对象也永远不会被 GC 处理。 “外部”。然而,如果其中一个引用是“弱”的,对象将被正确地 GC。

然而,Python确实有一个循环检测垃圾收集器(从 2.0 开始!),所以这不算:)

弱引用的另一个用途是缓存。 weakref 文档中提到了这一点:

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

如果 GC 决定销毁这些对象之一,而您需要它,则只需重新计算/重新获取数据即可。

The typical use for weak references is if A has a reference to B and B has a reference to A. Without a proper cycle-detecting garbage collector, those two objects would never get GC'd even if there are no references to either from the "outside". However if one of the references is "weak", the objects will get properly GC'd.

However, Python does have a cycle-detecting garbage collector (since 2.0!), so that doesn't count :)

Another use for weak references is for caches. It's mentioned in the weakref documentation:

A primary use for weak references is to implement caches or mappings holding large objects, where it’s desired that a large object not be kept alive solely because it appears in a cache or mapping.

If the GC decides to destroy one of those objects, and you need it, you can just recalculate / refetch the data.

稚气少女 2024-08-31 22:40:35
  • 弱引用是python中一个重要的概念,现在缺失了
    在 Java(java 1.5) 等语言中。
  • 在Observer设计模式中,一般Observable Object都要维护
    对 Observer 对象的弱引用。

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

  • 它对于实现缓存也非常有用。
  • Weak references is an important concept in python, which is missing
    in languages likes Java(java 1.5).
  • In Observer design pattern, generally Observable Object must maintain
    weak references to the Observer object.

    eg. A emits an event done() and B registers with A that, it want to
    listen to event done(). Thus, whenever done() is emitted, B is
    notified. But If B isn't required in application, then A must not
    become an hinderance in the garbage collection in A(since A hold the
    reference to B). Thus, if A has hold weak reference to B, and when
    all the references to A are away, then B will be garbage collected.

  • It's also very useful in implementing caches.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文