(This is a legacy post from the old website.)
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.
TL;DR version
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:
class MyClass(object):
instead of
class MyClass:
Long version
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.
I’ve found I occasionally run into an odd Vagrant error every now and then: