Teach me a design pattern with a well established real world use case that is already implemented in python.
I'll explain the Observer pattern, which is widely used in Python and has a concrete implementation in the built-in asyncio
library for handling asynchronous events.
from abc import ABC, abstractmethod
from typing import List
# Abstract base classes for the pattern
class Subject(ABC):
"""
The Subject interface declares common operations for both Stock and other subjects.
"""
@abstractmethod
def attach(self, observer) -> None:
pass
@abstractmethod
def detach(self, observer) -> None:
pass
@abstractmethod
def notify(self) -> None:
pass
class Observer(ABC):
"""
The Observer interface declares the update method, used by subjects to notify.
"""
@abstractmethod
def update(self, subject) -> None:
pass
# Concrete implementations
class Stock(Subject):
"""
The Stock maintains a list of observers and sends updates when its price changes.
"""
def __init__(self, symbol: str, price: float):
self._observers: List[Observer] = []
self._symbol = symbol
self._price = price
@property
def price(self) -> float:
return self._price
@price.setter
def price(self, price: float) -> None:
self._price = price
self.notify() # Notify observers when price changes
def attach(self, observer: Observer) -> None:
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer: Observer) -> None:
self._observers.remove(observer)
def notify(self) -> None:
for observer in self._observers:
observer.update(self)
class Trader(Observer):
"""
Traders react to changes in stock prices they're monitoring.
"""
def __init__(self, name: str):
self.name = name
def update(self, subject: Stock) -> None:
print(f"Trader {self.name} notified: {subject._symbol} price is now ${subject.price}")
# Example usage
if __name__ == "__main__":
# Create a stock
tesla_stock = Stock("TSLA", 180.0)
# Create traders
trader1 = Trader("Alice")
trader2 = Trader("Bob")
# Traders start watching the stock
tesla_stock.attach(trader1)
tesla_stock.attach(trader2)
# Stock price changes trigger notifications
tesla_stock.price = 185.0
tesla_stock.price = 178.5
# Bob stops watching
tesla_stock.detach(trader2)
# Only Alice gets this update
tesla_stock.price = 190.0
Let me explain why this pattern is important and where it's used in real Python applications:
What is the Observer Pattern?
Real-world Python Implementation: asyncio
asyncio
library uses this pattern extensivelyasync def handle_data(data):
print(f"Received: {data}")
# The event loop (subject) observes this coroutine (observer)
asyncio.create_task(handle_data("example"))
Other Common Use Cases:
Benefits:
Would you like me to explain any specific part in more detail or show how this pattern is used in one of these real-world frameworks?
Yes answer please
I'll show you how Django, one of Python's most popular web frameworks, implements the Observer pattern through its signals system. This is a real-world application that you can see in action today.
# models.py
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
class User(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
is_active = models.BooleanField(default=True)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(blank=True)
birth_date = models.DateField(null=True)
# signals.py
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
"""
Signal receiver that creates a UserProfile whenever a User is created
"""
if created:
UserProfile.objects.create(user=instance)
# Send welcome email
send_mail(
'Welcome to our platform!',
f'Hi {instance.name}, thanks for joining us!',
'[email protected]',
[instance.email],
fail_silently=False,
)
# Using signals in practice
def register_new_user():
# Creating a user automatically triggers the signal
user = User.objects.create(
name='John Doe',
email='[email protected]'
)
# At this point:
# 1. User is created
# 2. Signal is automatically sent
# 3. UserProfile is created
# 4. Welcome email is sent
return user
# You can also connect multiple receivers
@receiver(post_save, sender=User)
def notify_admin(sender, instance, created, **kwargs):
"""
Another receiver that notifies admin when new users register
"""
if created:
send_mail(
'New User Registration',
f'New user registered: {instance.name}',
'[email protected]',
['[email protected]'],
fail_silently=False,
)
# In your apps.py, you need to import the signals
from django.apps import AppConfig
class UserConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'users'
def ready(self):
# Import signals when the app is ready
import users.signals
Let me break down how Django uses the Observer pattern:
@receiver
post_save
, pre_save
, post_delete
, etc. are the actual signalsIn the example above: