首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用python WeakSet启用回调功能

使用python WeakSet启用回调功能
EN

Stack Overflow用户
提问于 2014-02-17 10:35:55
回答 2查看 2.9K关注 0票数 14

我正在研究能否在python中实现一个简单的回调功能。我想我也许可以使用weakref.WeakSet来实现这一点,但是很明显,我遗漏了一些东西,或者误解了一些东西。正如您在代码中看到的,我首先尝试使用'ClassA‘对象中的回调方法列表,但意识到这将使添加到回调列表中的对象保持活动状态。相反,我试着使用weakref.WeakSet,但这也不起作用(至少不是这样)。最后四行代码中的注释解释了我想要发生的事情。

有人能帮我吗?

代码语言:javascript
复制
from weakref import WeakSet
class ClassA:
    def __init__(self):
        #self.destroyCallback=[]
        self.destroyCallback=WeakSet()
    def __del__(self):
        print('ClassA object %d is being destroyed' %id(self))
        for f in self.destroyCallback:
            f(self)
class ClassB:
    def destroyedObjectListener(self,obj):
        print('ClassB object %d is called because obj %d is being destroyed'%(id(self),id(obj)))
a1=ClassA()
a2=ClassA()
b=ClassB()

a1.destroyCallback.add(b.destroyedObjectListener)
#a1.destroyCallback.append(b.destroyedObjectListener)
print('destroyCallback len() of obj: %d is: %d'%(id(a1),len(a1.destroyCallback))) # should be 1

a2.destroyCallback.add(b.destroyedObjectListener)
#a2.destroyCallback.append(b.destroyedObjectListener)
print('destroyCallback len() of obj: %d is: %d'%(id(a2),len(a2.destroyCallback))) # should be 1

del a1 # Should call b.destroyedObjectListener(self) in its __del__ method

del b # should result in no strong refs to b so a2's WeakSet should automatically remove added item

print('destroyCallback len() of obj: %d is: %d'%(id(a2),len(a2.destroyCallback))) # should be 0
del a2 # Should call __del__ method

更新:基于公认答案的解决方案可在github: git@github.com:thgis/PythonEvent.git上找到。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-02-21 17:52:44

不能创建对方法对象的弱引用。方法对象是短暂的;它们是在访问实例上的名称时动态创建的。看看描述符howto是如何工作的。

当您访问方法名称时,将为您创建一个新的方法对象,然后将该方法添加到WeakSet中,不再存在对它的其他引用,因此垃圾收集再次愉快地清理它。

你必须储存一些不那么短暂的东西。存储实例对象本身就能工作,然后对注册的回调调用预定义的方法:

代码语言:javascript
复制
def __del__(self):
    for f in self.destroyCallback:
        f.destroyedObjectListener(self)

并登记:

代码语言:javascript
复制
a1.destroyCallback.add(b)

还可以通过给b提供一个__call__方法,使它本身成为一个可调用的方法:

代码语言:javascript
复制
class ClassB:
    def __call__(self,obj):
        print('ClassB object %d is called because obj %d '
              'is being destroyed' % (id(self), id(obj)))

另一种方法是存储对基础函数对象的引用加上对实例的引用:

代码语言:javascript
复制
import weakref


class ClassA:
    def __init__(self):
        self._callbacks = []
    
    def registerCallback(self, callback):
        try:
            # methods
            callback_ref = weakref.ref(callback.__func__), weakref.ref(callback.__self__)
        except AttributeError:
            callback_ref = weakref.ref(callback), None
        self._callbacks.append(callback_ref)

    def __del__(self):
        for callback_ref in self._callbacks:
            callback, arg = callback_ref[0](), callback_ref[1]
            if arg is not None:
                # method
                arg = arg()
                if arg is None:
                    # instance is gone
                    continue
                callback(arg, self)
                continue
            else:
                if callback is None:
                    # callback has been deleted already
                    continue
                callback(self)

演示:

代码语言:javascript
复制
>>> class ClassB:
...     def listener(self, deleted):
...         print('ClassA {} was deleted, notified ClassB {}'.format(id(deleted), id(self)))
... 
>>> def listener1(deleted):
...     print('ClassA {} was deleted, notified listener1'.format(id(deleted)))
... 
>>> def listener2(deleted):
...     print('ClassA {} was deleted, notified listener2'.format(id(deleted)))
... 
>>> # setup, one ClassA and 4 listeners (2 methods, 2 functions)
... 
>>> a = ClassA()
>>> b1 = ClassB()
>>> b2 = ClassB()
>>> a.registerCallback(b1.listener)
>>> a.registerCallback(b2.listener)
>>> a.registerCallback(listener1)
>>> a.registerCallback(listener2)
>>> 
>>> # deletion, we delete one instance of ClassB, and one function
... 
>>> del b1
>>> del listener1
>>> 
>>> # Deleting the ClassA instance will only notify the listeners still remaining
... 
>>> del a
ClassA 4435440336 was deleted, notified ClassB 4435541648
ClassA 4435440336 was deleted, notified listener2
票数 21
EN

Stack Overflow用户

发布于 2014-02-20 17:45:52

尝试以下更改:

要更新WeakSet:

a1.destroyCallback.add(b)

因此,WeakSet保存了对b的引用。

然后在__del__方法ClassA中,触发如下回调:

代码语言:javascript
复制
for f in self.destroyCallback:
        f.destroyedObjectListener(self)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21826700

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档