使用 python WeakSet 启用回调功能 [英] using python WeakSet to enable a callback functionality

查看:18
本文介绍了使用 python WeakSet 启用回调功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

谁能帮我解决这个问题?

from weakref import WeakSetA类:def __init__(self):#self.destroyCallback=[]self.destroyCallback=WeakSet()def __del__(self):print('ClassA 对象 %d 正在被销毁' %id(self))对于 self.destroyCallback 中的 f:f(自我)B类:def destroyObjectListener(self,obj):print('ClassB 对象 %d 被调用,因为 obj %d 正在被销毁'%(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))) # 应该是 1a2.destroyCallback.add(b.destroyedObjectListener)#a2.destroyCallback.append(b.destroyedObjectListener)print('destroyCallback len() of obj: %d is: %d'%(id(a2),len(a2.destroyCallback))) # 应该是 1del a1 # 应该在其 __del__ 方法中调用 b.destroyedObjectListener(self)del b # 应该不会导致对 b 的强引用,因此 a2 的 WeakSet 应该会自动删除添加的项目print('destroyCallback len() of obj: %d is: %d'%(id(a2),len(a2.destroyCallback))) # 应该是 0del a2 # 应该调用 __del__ 方法

更新:基于已接受答案的解决方案可以在 github 上找到:git@github.com:thgis/PythonEvent.git

解决方案

您不能创建对方法对象的弱引用.方法对象是短暂的;它们是在您访问实例上的名称时动态创建的.请参阅描述符操作方法工作原理.>

当您访问一个方法名称时,会为您创建一个方法对象,然后当您将该方法添加到 WeakSet 时,不存在对其的其他引用不再,所以垃圾收集高兴地再次清理它.

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

def __del__(self):对于 self.destroyCallback 中的 f:f.destroyedObjectListener(self)

并注册:

a1.destroyCallback.add(b)

你也可以通过给它一个 __call__ 方法使 b itself 成为可调用的:

class ClassB:def __call__(self,obj):print('ClassB 对象 %d 被调用,因为 obj %d ''正在被销毁' % (id(self), id(obj)))

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

导入弱引用A类:def __init__(self):self._callbacks = []def registerCallback(self, callback):尝试:# 方法callback_ref = weakref.ref(callback.__func__), weakref.ref(callback.__self__)除了属性错误:callback_ref = weakref.ref(callback), 无self._callbacks.append(callback_ref)def __del__(self):对于 self._callbacks 中的 callback_ref:回调, arg = callback_ref[0](), callback_ref[1]如果 arg 不是 None:# 方法arg = arg()如果 arg 为 None:# 实例消失了继续回调(参数,自我)继续别的:如果回调为无:#回调已经被删除继续回调(自我)

演示:

<预><代码>>>>B类:...定义监听器(自我,已删除):... print('ClassA {} 被删除,通知 ClassB {}'.format(id(deleted), id(self)))...>>>def listener1(已删除):... print('ClassA {} 被删除,通知 listener1'.format(id(deleted)))...>>>def listener2(已删除):... print('ClassA {} 被删除,通知listener2'.format(id(deleted)))...>>># 设置,一个 ClassA 和 4 个监听器(2 个方法,2 个函数)...>>>a = ClassA()>>>b1 = ClassB()>>>b2 = ClassB()>>>a.registerCallback(b1.listener)>>>a.registerCallback(b2.listener)>>>a.registerCallback(listener1)>>>a.registerCallback(listener2)>>>>>>#删除,我们删除ClassB的一个实例和一个函数...>>>德尔 b1>>>删除监听器1>>>>>># 删除 ClassA 实例只会通知剩余的监听器...>>>德尔阿ClassA 4435440336 被删除,通知ClassB 4435541648ClassA 4435440336 被删除,通知listener2

I'm investigating if I can implement an easy callback functionality in python. I thought I might be able to use weakref.WeakSet for this, but there is clearly something I'm missing or have misunderstood. As you can see in the code I first tried with a list of call back methods in 'ClassA' objects, but realized that this would keep objects that have been added to the list of callbacks alive. Instead I tried using weakref.WeakSet but that doesnt do the trick either (at least not en this way). Comments in the last four lines of code explain what I want to happen.

Can anyone help me with this?

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

UPDATE: solution based on the accepted answer can be found on github: git@github.com:thgis/PythonEvent.git

解决方案

You cannot create weak references to method objects. Method objects are short lived; they are created on the fly as you access the name on the instance. See the descriptor howto how that works.

When you access a method name, a new method object is created for you, and when you then add that method to the WeakSet, no other references exist to it anymore, so garbage collection happily cleans it up again.

You'll have to store something less transient. Storing instance objects themselves would work, then call a predefined method on the registered callbacks:

def __del__(self):
    for f in self.destroyCallback:
        f.destroyedObjectListener(self)

and to register:

a1.destroyCallback.add(b)

You can also make b itself a callable by giving it a __call__ method:

class ClassB:
    def __call__(self,obj):
        print('ClassB object %d is called because obj %d '
              'is being destroyed' % (id(self), id(obj)))

Another approach would be to store a reference to the underlying function object plus a reference to the instance:

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)

Demo:

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

这篇关于使用 python WeakSet 启用回调功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆