11

我有一个项目,我试图在回调中使用弱引用,但我不明白我做错了什么。我创建了简化的测试,显示了我感到困惑的确切行为。

为什么在这个测试中 test_a 按预期工作,但是 self.MyCallbackB 的弱引用在类初始化和调用 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()
4

3 回答 3

12

你想要一个WeakMethod

可以在配方的讨论中找到您的解决方案不起作用的解释:

绑定方法的普通weakref.refs 并不像人们期望的那样工作,因为绑定方法是一等对象;除非存在对同一绑定方法的其他强引用,否则对绑定方法的弱引用是死的。

于 2009-03-01T08:16:06.513 回答
4

根据 Weakref 模块的文档:

在下文中,术语所指对象是指被弱引用引用的对象。

对对象的弱引用不足以使对象保持活动状态:当对所指对象的唯一剩余引用是弱引用时,垃圾收集可以自由地销毁所指对象并将其内存用于其他用途。

MyCallbackA 发生的事情是您在 A 的实例中持有对它的引用,这要归功于 -

self.MyCallbackA = MyCallbackA

现在,您的代码中没有对绑定方法 MyCallbackB 的引用。它仅作为未绑定方法保存在 a.__class__.__dict__ 中。基本上,当您执行 self.methodName 时,会创建一个绑定方法(并返回给您)。(AFAIK,绑定方法的工作方式类似于使用描述符(只读)的属性:至少对于新样式类。我敢肯定,旧样式类会发生类似的事情,即没有描述符。我将其留给更有经验的人来验证有关旧样式类的声明。)因此, self.MyCallbackB 在创建弱引用后立即死亡,因为没有对它的强引用!

我的结论基于:-

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

输出

Create MyCallbackB
Del MycallbackB
Done play with MyCallbackB
MyCallbackA

注意:
我尝试为旧样式类验证这一点。原来“print a.test_a.__get__”输出——

<method-wrapper '__get__' of instancemethod object at 0xb7d7ffcc>

适用于新旧风格的课程。所以它可能不是真正的描述符,只是类似描述符的东西。无论如何,重点是当你通过 self 访问一个实例方法时会创建一个绑定方法对象,除非你保持对它的强引用,否则它将被删除。

于 2009-03-01T08:27:33.647 回答
4

其他答案解决了原始问题中的原因,但要么不提供解决方法,要么参考外部站点。

在阅读了 StackExchange 上有关此主题的其他几篇文章(其中许多被标记为该问题的重复)之后,我终于找到了一个简洁的解决方法。当我知道我正在处理的对象的性质时,我使用了 weakref 模块;当我可能正在处理绑定方法时(就像在使用事件回调时在我的代码中发生的那样),我现在使用以下 WeakRef 类作为weakref.ref() 的直接替代品。我已经在 Python 2.4 到 Python 2.7 (包括 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__)
于 2013-03-18T01:05:09.763 回答