使用python WeakSet启用回调功能 [英] using python WeakSet to enable a callback functionality
问题描述
我正在调查是否可以在python中实现一个简单的回调函数。我认为我可以使用weakref.WeakSet这个,但有一些我缺失或有误解的东西。正如你可以在代码中看到的,我首先尝试了在ClassA对象中的回调方法列表,但意识到这将保持已添加到回调列表中的对象活着。相反我尝试使用weakref.WeakSet,但是没有做的伎俩(至少不是这样)。
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:基于接受的答案的解决方案可以在github上找到:git@github.com:thgis / PythonEvent.git
UPDATE: solution based on the accepted answer can be found on github: git@github.com:thgis/PythonEvent.git
推荐答案
您不能创建对方法对象的弱引用。方法对象是短暂的;它们是在您访问实例上的名称时即时创建的。请参阅描述符howto 的工作原理。
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.
当您访问方法名称时,会为您创建一个新方法对象,然后当您将该方法添加到 WeakSet
时,引用存在于它,所以垃圾收集很高兴再次清理它。
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)
:
a1.destroyCallback.add(b)
您还可以通过给予来使
方法: b
__call __
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)
b $ b
演示:
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屋!