Today I was trying to implement the observer pattern in Python, updating the observer with property methods, but my code was acting wonky. Specifically, my setter wasn’t calling a method it was supposed to be calling.
If properties aren’t functioning like they should, ensure that your classes are inheriting from object. Your classes don’t automatically inherit from object (or the featured base class) in Python like in some other languages (in python this is for the sake of backwards compatibility). Use:
Here’s the basic observer pattern in Python (Notice not inheriting from object):
class ObjectOfInterest(): def __init__(self): self.observers =  self._name = None def register_observer(self, observer): self.observers.append(observer) def _get_name(self): return self._name def _set_name(self, name): self._name = name self._update_observers() # I prefer this method of setting up properties over the @ syntax name = property(_get_name, _set_name) def _update_observers(self): for observer in self.observers: observer.update() class Observer(): def __init__(self, thing_to_watch): self.thing_to_watch = thing_to_watch self.thing_to_watch.register_observer(self) def update(self): print("updated.")
Pretty straight forward, right? However,
_update_observers() is never called. You will not see “updated.” printed to the console. Everything seems like it’s working; the value of name is being updated. So WTF?!
thing_to_watch = ObjectOfInterest() observer = Observer(thing_to_watch) print(thing_to_watch.name) # None thing_to_watch.name = 'Bilbo' print(thing_to_watch.name) # Bilbo thing_to_watch.name = 'Frodo' print(thing_to_watch.name) # Frodo
Remembering to inherit from object gets properties working right. Here again is the code with output but this time my classes do inherit from object and everything is right with the world.
thing_to_watch = ObjectOfInterest() observer = Observer(thing_to_watch) print(thing_to_watch.name) # None thing_to_watch.name = 'Bilbo' # updated. print(thing_to_watch.name) # Bilbo thing_to_watch.name = 'Frodo' # updated. print(thing_to_watch.name) # Frodo
This is a sloppy mistake that had me guessing for a good bit. Nothing was out of place and yet my results were borked.