Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
375: The Command Pattern
The Command Pattern is one of those things that feels like "over-engineering" until the moment your boss asks you to implement a multi-level Undo system or a macro recording feature. At its core, the pattern is about turning a request into a standalone object. Instead of calling a method on an object directly, you wrap that call inside a "Command" object. This decouples the object that triggers the action from the object that actually knows how to perform it.
Setting up our smart home devices
To make this concrete, let's build a simple smart home controller. We have a few different devices—a light and a fan—that all do different things. In pattern terminology, these are our "Receivers." They hold the actual business logic.
class Light:
def turn_on(self):
print("The light is shining bright!")
def turn_off(self):
print("The light is now off.")
class Fan:
def start(self):
print("The fan is spinning.")
def stop(self):
print("The fan has stopped.")
Creating a unified way to trigger actions
Now, if we just call light.turn_on() from our main app, we're tightly coupled. If we want to add a "Macro" that turns on the light and the fan simultaneously, we'd have to write custom logic for every possible combination. Instead, I'll create a Command interface. In Python, we don't need a formal interface, but using an abstract base class makes our intentions clear.
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
Wrapping the device logic in commands
Now I'll create concrete commands for our devices. Notice how the command object doesn't do the work itself; it just tells the receiver what to do. It's essentially a "pointer" to a specific action.
class LightOnCommand(Command):
def __init__(self, light):
self.light = light
def execute(self):
self.light.turn_on()
def undo(self):
self.light.turn_off()
class FanOnCommand(Command):
def __init__(self, fan):
self.fan = fan
def execute(self):
self.fan.start()
def undo(self):
self.fan.stop()
Where I messed up the Invoker
Next, I need an "Invoker"—the thing that actually triggers the command (like a remote control). When I first wrote this, I tried to be "helpful" by making the remote check what kind of command it was holding so it could call a specific method. It looked something like this:
# DON'T DO THIS
class RemoteControl:
def submit(self, command):
if isinstance(command, LightOnCommand):
command.execute()
elif isinstance(command, FanOnCommand):
command.execute()
I realized immediately that I'd defeated the entire purpose of the pattern. If I add ten more devices, I have to keep updating this if-elif chain. The whole point of the Command Pattern is that the Invoker shouldn't care what the command is, only that it has an execute() method. I stripped that logic out and simplified it to a single line.
class RemoteControl:
def __init__(self):
self.history = []
def press_button(self, command):
command.execute()
self.history.append(command)
def press_undo(self):
if self.history:
command = self.history.pop()
command.undo()
else:
print("Nothing to undo!")
Putting it all together
Now we can treat actions as data. I can pass them around, store them in a list, or trigger them based on a timer. The remote doesn't know it's talking to a light or a fan; it just knows it's executing a command.
# Setup
living_room_light = Light()
bedroom_fan = Fan()
light_on = LightOnCommand(living_room_light)
fan_on = FanOnCommand(bedroom_fan)
remote = RemoteControl()
# Execution
remote.press_button(light_on) # The light is shining bright!
remote.press_button(fan_on) # The fan is spinning.
# Undo
remote.press_undo() # The fan has stopped.
remote.press_undo() # The light is now off.📋 Practical Task
Exercise: Implementing a Macro Command for a "Movie Mode"
In the smart home example above, we can only execute one command at a time. Your task is to implement a MacroCommand. This is a special kind of command that holds a list of other commands and executes them all in sequence when its execute() method is called, and undoes them all (in reverse order) when its undo() method is called.
Requirements:
- Create a class
MacroCommandthat inherits fromCommand. - It should take a list of commands in its
__init__method. execute()` should loop through the commands and call their respectiveexecute()methods.undo()` should loop through the commands in reverse order and call theirundo()methods.- Test your
MacroCommandby creating a "Movie Mode" that turns on a light and starts a fan simultaneously using theRemoteControl.
There are no comments for now.