如何存储回调方法?

2 投票
4 回答
3042 浏览
提问于 2025-04-15 15:39

我正在尝试存储一些方法的回调,但如果直接引用这些方法,绑定的对象会一直保持活着。所以我试着使用弱引用来引用这些方法,但这似乎不太可能。

所以

  • 为什么我不能对方法使用弱引用?(见下面的例子)

  • 保持方法引用的最佳方式是什么?标准库里有相关的东西吗?还是我必须将函数和对象的引用分开?

例子:

import weakref

class A(object):
    def m(self): pass

a = A()

import weakref

class A(object):
    def m(self): pass

a = A()

rm = weakref.ref(a.m)
print "is weak ref to method dead?",rm() is None
print "Q1. why can't i keep weakref to bound method?"

ra = weakref.ref(a)
m = a.m
print "delete object"
del a
print "is object dead?",ra() is None
print "delete method"
del m
print "is object dead?",ra() is None
print "Q2. hmmm so i am stuck i can't keep a ref as it stops the object from gc, but weakref to method isn't working?"

4 个回答

2

在《Python Cookbook》的第6.10个食谱中,标题是“保持对绑定方法的引用而不阻碍垃圾回收”,提供了一个相当全面但简洁的讨论和解决方案。你可以在网上阅读这部分内容(在Google Books上)这里。我们要感谢Knapka、Jolliton和Nicodemus提供的这个食谱(部分内容来自另一位回答者提到的原始activestate食谱),当然,像往常一样,讨论的整体流程和打印时选择的具体代码版本是由我们(我、我的妻子Anna和David Ascher)负责的,所以如果这些内容有问题,那就是我们的错;-)。

3

既然这个方法是和对象绑定在一起的,如果对象不存在,你觉得这个方法该怎么用呢?那时候,self里面会有什么呢?

如果你在这个方法里不需要用到对象,那就把它改成类方法。这样即使你有普通的引用指向这个方法,你的对象也会被垃圾回收掉。

0

我在这里问过同样的问题!在我的问题中,我提到了GObject,但我意识到这其实是Python中一个普遍存在的问题!在那儿得到了lioro的帮助,下面是我当前代码中使用的内容。一些重要的要点:

  • 你不能对方法对象使用弱引用。你需要对实例和它的函数属性使用弱引用,或者简单地使用方法名(就像我在下面的代码中做的那样)

  • 你可以添加一些机制来注销回调,当你连接的对象消失时。如果不这样做,WeakCallback对象会继续存在,并在事件发生时执行一个空的方法。

.

class WeakCallback (object):
    """A Weak Callback object that will keep a reference to
    the connecting object with weakref semantics.

    This allows object A to pass a callback method to object S,
    without object S keeping A alive.
    """
    def __init__(self, mcallback):
        """Create a new Weak Callback calling the method @mcallback"""
        obj = mcallback.im_self
        attr = mcallback.im_func.__name__
        self.wref = weakref.ref(obj, self.object_deleted)
        self.callback_attr = attr
        self.token = None

    def __call__(self, *args, **kwargs):
        obj = self.wref()
        if obj:
            attr = getattr(obj, self.callback_attr)
            attr(*args, **kwargs)
        else:
            self.default_callback(*args, **kwargs)

    def default_callback(self, *args, **kwargs):
        """Called instead of callback when expired"""
        pass

    def object_deleted(self, wref):
        """Called when callback expires"""
        pass

使用说明:

# illustration how I typically use it
weak_call = WeakCallback(self._something_changed)
long_lived_object.connect("on_change", weak_call)

我在自己创建的子类中使用了WeakCallback.token属性来管理在连接者消失时如何断开回调。

撰写回答