我有一个项目,我试图使用带有回调的weakrefs,我不明白我做错了什么。我创建了简化测试,显示了我与之混淆的确切行为。
为什么在这个测试中test_a按预期工作,但self.MyCallbackB的weakref在类初始化和调用test_b之间消失了?我想只要实例(a)存在,对self.MyCallbackB的引用应该存在,但它不存在。
import weakref
class A(object):
def __init__(self):
def MyCallbackA():
print 'MyCallbackA'
self.MyCallbackA = MyCallbackA
self._testA = weakref.proxy(self.MyCallbackA)
self._testB = weakref.proxy(self.MyCallbackB)
def MyCallbackB(self):
print 'MyCallbackB'
def test_a(self):
self._testA()
def test_b(self):
self._testB()
if __name__ == '__main__':
a = A()
a.test_a()
a.test_b()
答案 0 :(得分:12)
你想要一个WeakMethod。
解释为什么您的解决方案不起作用的解释可以在配方的讨论中找到:
正常的weakref.refs到绑定方法并不像人们期望的那样工作,因为绑定方法是第一类对象;除了对同一绑定方法的其他强引用之外,对绑定方法的弱引用仍然是
。
答案 1 :(得分:4)
根据Weakref模块的文档:
在下文中,术语“指示对象”表示所引用的对象 弱引用。
对象的弱引用不是 足以让对象保持活力:何时 唯一剩下的参考文献 指称是弱引用,垃圾 收藏是免费的破坏 指称和重用它的记忆 别的什么。
MyCallbackA发生的事情是你在A的实例中持有对它的引用,这要归功于 -
self.MyCallbackA = MyCallbackA
现在,代码中没有对绑定方法MyCallbackB的引用。它仅作为一种非约束方法在.__类__.__ dict__中保存。基本上,当你执行self.methodName时,会创建一个绑定方法(并返回给你)。 (AFAIK,一个绑定方法就像一个属性 - 使用描述符(只读):至少对于新的样式类。我相信,类似的东西,即w / o描述符发生在旧式类。我会留下它有经验的人可以验证关于旧式类的声明。)所以,self.MyCallbackB一旦创建了weakref就会死掉,因为没有强烈的引用!
我的结论基于: -
import weakref
#Trace is called when the object is deleted! - see weakref docs.
def trace(x):
print "Del MycallbackB"
class A(object):
def __init__(self):
def MyCallbackA():
print 'MyCallbackA'
self.MyCallbackA = MyCallbackA
self._testA = weakref.proxy(self.MyCallbackA)
print "Create MyCallbackB"
# To fix it, do -
# self.MyCallbackB = self.MyCallBackB
# The name on the LHS could be anything, even foo!
self._testB = weakref.proxy(self.MyCallbackB, trace)
print "Done playing with MyCallbackB"
def MyCallbackB(self):
print 'MyCallbackB'
def test_a(self):
self._testA()
def test_b(self):
self._testB()
if __name__ == '__main__':
a = A()
#print a.__class__.__dict__["MyCallbackB"]
a.test_a()
输出
创建MyCallbackB
Del MycallbackB
用MyCallbackB做完了 MyCallbackA
注意:
我试着为老式课程验证这个。结果是“打印a.test_a .__ get__”
输出 -
<method-wrapper '__get__' of instancemethod object at 0xb7d7ffcc>
适用于新旧样式类。所以它可能不是一个描述符,只是描述符之类的东西。在任何情况下,关键是当你通过self访问实例方法时会创建一个bound-method对象,除非你保持对它的强引用,否则它将被删除。
答案 2 :(得分:3)
其他答案解决原始问题中的 why ,但要么不提供变通方法,要么引用外部网站。
在完成这个主题的StackExchange上的其他几篇文章之后,其中很多都被标记为这个问题的重复,我终于找到了一个简洁的解决方法。当我知道我正在处理的对象的性质时,我使用weakref模块;当我可能会处理绑定方法时(就像我在使用事件回调时的代码中所发生的那样),我现在使用以下WeakRef类作为weakref.ref()的直接替代。我已经使用Python 2.4测试了这个,包括Python 2.7,但不是在Python 3.x上。
class WeakRef:
def __init__ (self, item):
try:
self.method = weakref.ref (item.im_func)
self.instance = weakref.ref (item.im_self)
except AttributeError:
self.reference = weakref.ref (item)
else:
self.reference = None
def __call__ (self):
if self.reference != None:
return self.reference ()
instance = self.instance ()
if instance == None:
return None
method = self.method ()
return getattr (instance, method.__name__)