为什么weakref不能在这个绑定方法上工作?

时间:2009-03-01 07:23:21

标签: python weak-references

我有一个项目,我试图使用带有回调的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()

3 个答案:

答案 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__)