Python Design Patterns — Proven Solutions
Design patterns are reusable solutions to common problems. Python's dynamic nature often simplifies traditional patterns.
Learning Objectives
- Implement common design patterns in Python
- Know when to use each pattern
- Apply Pythonic alternatives to classic patterns
Singleton Pattern
# Classic
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# Pythonic — use module-level variable
_config = None
def get_config():
global _config
if _config is None:
_config = load_config()
return _config
Factory Pattern
class Dog:
def speak(self): return "Woof!"
class Cat:
def speak(self): return "Meow!"
def animal_factory(animal_type):
animals = {"dog": Dog, "cat": Cat}
return animals[animal_type]()
pet = animal_factory("dog")
print(pet.speak()) # "Woof!"
Observer Pattern
class EventEmitter:
def __init__(self):
self._listeners = {}
def on(self, event, callback):
self._listeners.setdefault(event, []).append(callback)
def emit(self, event, *args, **kwargs):
for callback in self._listeners.get(event, []):
callback(*args, **kwargs)
emitter = EventEmitter()
emitter.on("data", lambda x: print(f"Received: {x}"))
emitter.emit("data", "hello") # "Received: hello"
Strategy Pattern
def bubble_sort(data): return sorted(data)
def quick_sort(data): return sorted(data)
def merge_sort(data): return sorted(data)
def sort_data(data, strategy=bubble_sort):
return strategy(data)
result = sort_data([3, 1, 2], strategy=quick_sort)
Key Takeaways
- Prefer Pythonic solutions over classic GoF patterns
- Use modules for Singleton behavior
- Use first-class functions for Strategy
- Use callables for Factory
- Observer is useful for event-driven systems