Observer pattern

RoadmapsPython

Overview

The Observer pattern lets one object automatically notify other objects when something happens. Imagine a YouTube channel. When a creator uploads a video, millions of subscribers get a notification. The subscribers do not have to check the channel every five minutes. The channel (the Subject) keeps a list of subscribers (the Observers) and alerts them all at once. Here is a simple example for a news alert system: ```python class NewsAgency: def __init__(self): self.subscribers = [] def subscribe(self, person): self.subscribers.append(person) def notify_all(self, news): for person in self.subscribers: person.receive(news) ``` Now, we create subscribers and the agency. When news breaks, everyone gets it: ```python class Person: def __init__(self, name): self.name = name def receive(self, news): print(f"{self.name} received: {news}") agency = NewsAgency() agency.subscribe(Person("Rahul")) agency.subscribe(Person("Anjali")) agency.notify_all("Rain tomorrow!") ``` Both Rahul and Anjali will print the message. The `NewsAgency` does not care who they are, as long as they have a `receive` method. Let us practice. Here is a faded example. Fill in the blank to alert all the observers. ```python class GroupChat: def __init__(self): self.members = [] def send_message(self, text): for m in self.members: ____(text) # Call the member's receive method ``` The rule to remember: **The Observer pattern is a publisher keeping a list of subscribers and looping through them to send updates.** There is a sharp edge here. If you subscribe an object but forget to remove it later, the publisher keeps holding onto it forever. This is called a memory leak. If a user logs out, you must remove them from the list.

It keeps your code uncoupled. The publisher does not need to know the exact details of every subscriber. It just sends the message. You can add 100 new subscribers without changing the publisher code.

Where used: User interface buttons (clicking a button notifies the app), Sending emails when a database record changes, Real-time chat applications

Why learn this

Code walkthrough

class Alarm:
    def __init__(self):
        self.listeners = []
    def trigger(self):
        for listener in self.listeners:
            listener.sound()

class Siren:
    def sound(self): print("WEE WOO")

class Phone:
    def sound(self): print("Beep beep")

alarm = Alarm()
alarm.listeners = [Siren(), Phone()]
alarm.trigger()

Focus: for listener in self.listeners:

Aha moment

class Publisher:
    def __init__(self):
        self.subs = []
    def alert(self):
        for sub in self.subs:
            sub()

def print_hello():
    print("Hello!")

pub = Publisher()
pub.subs.append(print_hello)
pub.alert()

Prediction: What does this print? Look closely at what we appended to the list.

Common guess: It crashes because `print_hello` is not an object.

In Python, functions ARE objects! You do not always need a class to be an observer. You can just append a simple function to the list, and the publisher can call it.

Common mistakes

Recall questions

Understanding checks

What does this code print?

Buying now! Buying now!

The Store has two Shoppers in its list. When `sale()` is called, it loops through the list and calls `notify()` on both of them.

Why does this code crash?

Because it tries to call the `Display` object like a function `screen(temp)` instead of calling its method `screen.show(temp)`.

The publisher must call a specific method on the observer. Objects cannot usually be called directly with parentheses unless they have special magic methods.

Practice tasks

Add a new observer

Given this code, create a new `EmailLogger` object and subscribe it to the `system` so that it also receives the error.

Continue learning

Previous: Class Creation and Instantiation

Return to Python Roadmap