python弱引用_何时在Python中使用弱引用?

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

问题

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

您可以尝试这样的实现: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控件。

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

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

解决方案

有一个很好的类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(),以确保接收器立即得到真正的清理。这里需要它,因为现在有一个强引用循环:bound方法引用接收方,反之亦然。

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

如果您真的需要,可以通过将绑定方法替换为自定义函数对象来删除强引用循环,该对象也将其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])

让我们把这个逻辑放到一个helper函数中: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循环。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值