Как правильно реализовать 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) в деструкторе наблюдателя.

Другие вопросы по тегам