使用python WeakSet启用回调功能
我正在研究是否可以在python中实现简单的回调功能。我想我可能可以使用weakref.WeakSet来实现这一点,但很明显,我遗漏了一些东西或误解了一些东西。正如您在代码中看到的,我第一次尝试在“ClassA”对象中使用回调方法列表,但意识到这将使添加到回调列表中的对象保持活动状态。相反,我尝试使用weakref.WeakSet,但这也不起作用(至少不是这样)。最后四行代码中的注释解释了我希望发生的事情 有人能帮我吗使用python WeakSet启用回调功能,python,callback,destructor,weak-references,Python,Callback,Destructor,Weak References,我正在研究是否可以在python中实现简单的回调功能。我想我可能可以使用weakref.WeakSet来实现这一点,但很明显,我遗漏了一些东西或误解了一些东西。正如您在代码中看到的,我第一次尝试在“ClassA”对象中使用回调方法列表,但意识到这将使添加到回调列表中的对象保持活动状态。相反,我尝试使用weakref.WeakSet,但这也不起作用(至少不是这样)。最后四行代码中的注释解释了我希望发生的事情 有人能帮我吗 from weakref import WeakSet class Clas
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尝试以下更改: 要更新WeakSet,请执行以下操作:
a1.销毁回调。添加(b)
所以WeakSet包含对b的引用
然后在ClassA的\uuu del\uu
方法中,触发如下回调:
for f in self.destroyCallback:
f.destroyedObjectListener(self)
不能创建对方法对象的弱引用。方法对象是短期的;它们是在您访问实例上的名称时动态创建的。看看它是如何工作的 当您访问一个方法名时,会为您创建一个新的方法对象,然后将该方法添加到
弱集
,不再存在对该方法的其他引用,因此垃圾收集会很高兴地再次将其清除
你必须储存一些不那么短暂的东西。存储实例对象本身可以工作,然后对已注册的回调调用预定义的方法:
def __del__(self):
for f in self.destroyCallback:
f.destroyedObjectListener(self)
以及登记:
a1.destroyCallback.add(b)
您还可以通过给\uuu call\uuu
方法使b
本身成为可调用的:
class ClassB:
def __call__(self,obj):
print('ClassB object %d is called because obj %d '
'is being destroyed' % (id(self), id(obj)))
另一种方法是存储对底层函数对象的引用和对实例的引用:
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)
演示:
谢谢你的回复,格伦。我想那会有用的。。我想我只能为一个对象创建一个weakref,而不是我尝试的对象方法。您的方法将创建一个weakref to b,因此可以工作,但必须显式地编写函数名“destroyedObjectListener”,事件驱动的想法就消失了。但你让我得出的结论是,问题在于我试图创建一个类方法的weakref。现在我有了你的建议,它似乎很有效。我将解决方案包装在一个事件类中,并实现了add()、remove()和signal(),以减少使用。也许它会扩大。如果有人感兴趣,可以使用以下方法从github下载解决方案:git@github.com:thgis/PythonEvent.git太棒了!我只建议使用
getattr(回调,'.\u self\u',None)
而不是exception.@ShitalShah:不,因为这样会创建对None
的弱引用。我们希望存储None
本身,而不是对它的弱引用。这样,您就可以检测方法和函数之间的差异。
>>> 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