Card 06/ 09
Observer Classes vs Callbacks in Python
A list of functions is a subject; append is attach.
In Python an observer with one method can be a plain function, a callback, and the subject can be a list of them. Appending to the list attaches, removing detaches, and calling each in turn notifies. This card puts that form beside the class form, and sets out what each loses.
from collections.abc import Callable
Listener = Callable[[int], None]
class Thermostat:
def __init__(self) -> None:
self.listeners: list[Listener] = []
def set_temperature(self, celsius: int) -> None:
for listener in self.listeners:
listener(celsius)
def show(celsius: int) -> None:
print(f"display: {celsius} C")
thermostat = Thermostat()
thermostat.listeners.append(show)
thermostat.listeners.append(lambda celsius: print("heating on") if celsius < 18 else None)
thermostat.set_temperature(21)
thermostat.set_temperature(16)display: 21 C
display: 16 C
heating onThe thermostat has no Observer interface and no observer classes. show and a lambda subscribe by being appended, and each reading reaches both: the display shows 21 and then 16, and the heating line appears only for the reading below 18.
| Concern | Observer classes | Callbacks |
|---|---|---|
| Code per observer | A class with an update method | A function or a lambda |
| State between notifications | Fields on the observer | A closure, or a bound method of an object that holds it |
| Several kinds of event | Several methods on one interface, such as order_placed and order_cancelled | One list per event |
| Detaching | detach(observer) with the object you attached | Remove the same function object; a lambda kept nowhere cannot be removed |
| What the type says | The interface names the events | Callable[[int], None] says only the arguments |
The fourth row causes the most trouble in practice. A lambda passed straight to append exists only in the list, so nothing else holds it and nothing can name it to remove it later. Keep a reference to any callback that will need detaching, or pass a named function.
Use callbacks when each subscriber reacts to one event and needs no state of its own, which covers most user interface handlers and hooks. Use observer classes when the subject has several related events, and the interface that names them together is worth having.
← → move through sections and cards