In 1996, Peter Norvig noted that 16 out of the 23 design patterns in the original Design Patterns book (Gamma et al.) are "invisible or simpler" in languages with first-class functions (like Lisp, or Python). Python's ability to treat functions as objects makes many heavyweight OOP patterns redundant.
The Goal: Define a family of algorithms, encapsulate each one, and make them interchangeable.
Classic OOP implementation:
- Context object
Orderholds a reference to a Strategy interface. - Abstract Base Class
PromotionStrategydefines a.discount()method. - Concrete subclasses
VIPPromotion,BulkPromotionimplement.discount().
Pythonic implementation:
- A strategy is just a
Callablepassed as a parameter. - No Abstract Base Classes. No single-method subclasses.
- You just pass
vip_promo_functiondirectly to theOrder.
Benefits: Less boilerplate. Functions are lighter than class instances.
The Goal: Decouple the object that invokes an operation from the one that knows how to perform it.
Classic OOP implementation:
An Invoker holds an instance of an AbstractCommand and calls its .execute() method.
Pythonic implementation:
An Invoker holds a Callable and calls func(). If the command requires state (arguments to be executed later), you can use functools.partial or a closure to freeze the arguments, entirely bypassing the need for a custom class with an __init__ and execute() method.
If you need to execute a sequence of commands, you don't need a complex Composite pattern. A MacroCommand in Python can simply be a class that stores a list of Callable objects and implements __call__ to iterate through them.
If you find yourself writing a class with:
- An
__init__method. - Exactly one other method (like
execute,run, orapply).
...you are likely writing Java in Python. That class should almost certainly be a simple function or a closure.