Как правильно реализовать Observer в python, когда наблюдатель [должен быть] уничтожен
Я реализую наблюдаемый шаблон наблюдателя в Python:
Это наблюдаемый класс:
class Observable(object):
def __init__(self, value):
self.value = value
self.observers = []
def set(self, value):
old = self.value
self.value = value
self.notifyObservers(old, self.value)
def get(self):
return self.value
def addObserver(self, o):
self.observers.append(o)
def removeObserver(self, o):
if o in self.observers:
self.observers.remove(o)
def notifyObservers(self, old, new):
for o in self.observers:
o.valueChanged(old, new)
а это наблюдатель
class Observer(object):
def __init__(self, foo):
self.foo = foo
self.foo.addObserver(self)
def __del__(self):
print('Observer.__del__ called')
self.foo.removeObserver(self)
def valueChanged(self, old, new):
print('foo changed from %s to %s' % (old, new))
Код работает как положено.
Но мне нужно Observer
быть уничтоженным (т. е. когда на него не ссылаются, он должен удалить себя из списка наблюдателей в Observable
объект).
Проблема в том, что с этим кодом Observer.__del__
никогда не вызывается, если Observer
находится в списке наблюдателей некоторых Observable
объект.
Обратите внимание, что я не обязательно уничтожаю Observer
явно, он также не будет ссылаться из-за присваивания переменной, таким образом вызывая removeObserver()
явно до уничтожения нежизнеспособен.
Если я закомментирую self.foo.addObserver(self)
, то нет никаких дополнительных ссылок на Observer
и звонит del
на это позвонит Observer.__del__
,
Тестовый сценарий для этого сценария:
foo = Observable(23)
bar = Observer(foo)
foo.set(44)
bar = None
foo.set(1)
у этого есть два результата:
- если
self.foo.addObserver(self)
не закомментировано, печатаетсяfoo changed from 23 to 44
а такжеfoo changed from 44 to 1
- если
self.foo.addObserver(self)
закомментировано, печатаетObserver.__del__ called
2 ответа
Кажется, что слабая ссылка решит вашу проблему. Вы изменяете наблюдателей для управления слабыми ссылками, например, заменяя list
в weakref.WeakKeyDictionary
или путем реализации некоторого другого контейнера со слабой ссылкой. Кстати, использование хэшированного типа, такого как словарь, также будет лучше, чем список, так как удаление наблюдателя будет гораздо более эффективным.
Решение: (изменено Observable.observers
в weakref.WeakKeyDictionary
)
class Observable(object):
def __init__(self, value):
self.value = value
self.observers = weakref.WeakKeyDictionary()
def set(self, value):
old = self.value
self.value = value
self.notifyObservers(old, self.value)
def get(self):
return self.value
def addObserver(self, o):
self.observers[o] = 1
def removeObserver(self, o):
del self.observers[o]
def notifyObservers(self, old, new):
for o in self.observers:
o.valueChanged(old, new)
Также не требуется звонить .removeObserver(self)
в деструкторе наблюдателя.