Python Observer vs Pub/Sub — Core Concepts
The Core Difference
Both patterns solve the same problem: notifying interested parties when something changes. The critical difference is coupling.
Observer pattern: The subject (publisher) holds direct references to its observers (subscribers). When state changes, the subject iterates through its list and calls each observer.
Pub/Sub pattern: A broker (message bus, event channel) sits between publishers and subscribers. Publishers send messages to the broker. Subscribers register interest with the broker. Neither side knows about the other.
Observer Pattern Structure
The classic Observer has two roles:
- Subject — owns state, maintains a list of observers, notifies them on changes
- Observer — registers with a subject, implements an
update()method
The relationship is direct. The subject calls observer.update() on each registered observer. Observers typically hold a reference back to the subject to query new state.
Key characteristics:
- Observers register with the subject directly
- Notification is synchronous (by default)
- The subject knows how many observers it has
- Observers can be tightly typed (implement a specific interface)
Pub/Sub Structure
Pub/Sub introduces a third component:
- Publisher — emits messages to a topic/channel on the broker
- Broker — routes messages from topics to subscribers
- Subscriber — registers interest in specific topics with the broker
Key characteristics:
- Publishers and subscribers don’t reference each other
- Communication can be asynchronous and even cross-process
- The broker handles routing, filtering, and delivery guarantees
- Topics are typically string-based (loose coupling)
Comparison Table
| Aspect | Observer | Pub/Sub |
|---|---|---|
| Coupling | Subject ↔ Observer | Publisher → Broker ← Subscriber |
| Communication | Direct method call | Via message broker |
| Typical scope | Single process | Single process to distributed |
| Delivery | Synchronous by default | Often asynchronous |
| Type safety | Strong (interface-based) | Weak (string topics, generic payloads) |
| Debugging | Easier to trace | Harder — indirection through broker |
| Scalability | Limited by direct references | Scales across processes/machines |
When Observer Fits Better
- UI data binding — a view watching a model for changes
- Single-object state tracking — monitoring changes to one specific object
- Small, in-process systems — where simplicity wins over flexibility
- When you need guaranteed delivery order — synchronous notification is predictable
When Pub/Sub Fits Better
- Multiple independent subsystems — services that shouldn’t know about each other
- Plugin architectures — third-party code subscribing to events
- Cross-process or distributed systems — when observers aren’t in the same memory space
- High fan-out — when dozens of subscribers react to the same event type
Python Standard Library: Observer Without a Class
Python doesn’t include a formal Observer interface, but the pattern appears everywhere:
- Property setters with callbacks — trigger actions when attributes change
__set_name__descriptors — react to class creationatexitmodule — register functions called on interpreter shutdown
For Pub/Sub, Python offers:
queue.Queue— in-process message passing between threadsasyncio.Queue— async equivalentmultiprocessing.Queue— cross-process messaging
Common Misconception
“Pub/Sub is always better because it’s more decoupled.” More decoupling isn’t always better. Observer’s tight coupling means you get type safety, easier debugging, and simpler code. If your observers are all in the same process and you need to trace notification order, Observer is the pragmatic choice. Pub/Sub’s indirection is overhead you shouldn’t pay unless you need the flexibility it provides.
The Hybrid Reality
In practice, many Python systems blur the line. Flask’s signals (via blinker) look like Pub/Sub (named string signals with a dispatcher) but behave like Observer (synchronous, in-process, direct function calls). Django’s signals work similarly.
Event emitter libraries like pyee sit in the middle — they use string event names (Pub/Sub-like) but maintain direct listener references (Observer-like).
The choice isn’t binary. It’s a spectrum from “direct reference notification” to “fully mediated messaging,” and you pick the point that matches your coupling requirements.
One thing to remember: Observer means the sender knows its receivers. Pub/Sub means the sender doesn’t. Choose Observer when you want simplicity and traceability in a single process; choose Pub/Sub when you need decoupling across boundaries.
See Also
- Python Event Emitter Patterns How Python programs shout 'something happened!' so other parts of the code can react — like a school bell that tells everyone it's recess.
- Python Rxpy Reactive Programming How RxPY lets Python code react to streams of data the way a news ticker reacts to breaking stories — automatically and in real time.
- Python State Machines Transitions How the transitions library helps Python code manage things that change between clear stages — like a traffic light that only goes green → yellow → red.
- Ci Cd Why big apps can ship updates every day without turning your phone into a glitchy mess — CI/CD is the behind-the-scenes quality gate and delivery truck.
- Containerization Why does software that works on your computer break on everyone else's? Containers fix that — and they're why Netflix can deploy 100 updates a day without the site going down.