Ответ 1
Вы не можете создавать слабые ссылки на объекты метода. Объекты метода недолговечны; они создаются "на лету", когда вы получаете доступ к имени в экземпляре. См. дескриптор howto, как это работает.
Когда вы обращаетесь к имени метода, для вас создается новый объект метода, и когда вы добавляете этот метод в WeakSet
, больше никаких ссылок на него не существует, поэтому сбор мусора благополучно очищает его снова.
Вам нужно будет хранить что-то меньшее переходное. Хранение объектов экземпляра будет работать, затем вызовите предопределенный метод для зарегистрированных обратных вызовов:
def __del__(self):
for f in self.destroyCallback:
f.destroyedObjectListener(self)
и зарегистрировать:
a1.destroyCallback.add(b)
Вы также можете сделать b
себя вызываемым, указав метод __call__
:
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)
Демо:
>>> 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