
The command pattern turns an action into an object. Instead of calling add(1, 1) directly, you build an AddCmd(1, 1) object and ask it to execute() later. That indirection sounds like extra work until you need to queue actions, log them, undo them or pick one by name at runtime. Then it is the simplest design available.
This post builds a small calculator with the pattern in Python: an abstract command, two concrete commands, a registry that lets you look commands up by name and a test that proves it works. The code is split across four modules the way you would in a real project.
When the pattern earns its keep
Reach for the command pattern when you have a set of operations that share a shape and you want to treat them uniformly. Typical cases:
- A command-line tool or chat bot that maps a word the user typed to a function.
- An undo stack, where every action knows how to reverse itself.
- A job queue, where actions are created in one place and run in another.
- A plugin system, where new operations register themselves without editing a central
if/elifchain.
The calculator below is deliberately tiny so the structure stays visible. Swap add and subtract for deploy and rollback and nothing about the design changes.
The abstract command
Every command shares two things: a name to look it up by and an execute() method that does the work. Put that contract in an abstract base class in src/interface.py:
# src/interface.py
import abc
class CalcCmdAbs(metaclass=abc.ABCMeta):
def __init__(self, *args, **kwargs):
self._args = args
self._kwargs = kwargs
@property
@abc.abstractmethod
def name(self):
raise NotImplementedError()
def execute(self):
raise NotImplementedError()
abc.ABCMeta makes name mandatory: Python refuses to instantiate a subclass that does not define it. execute() is left as a plain method that raises, so a subclass that forgets it fails loudly at call time rather than at import time. The constructor stores whatever arguments it is given so that commands can be replayed or logged later.
Concrete commands and a registry
The two real commands live in src/commands.py, along with a decorator that registers each class in a module-level list as it is defined:
# src/commands.py
from .interface import CalcCmdAbs
OPERATORS = list()
def register_operator(cls):
OPERATORS.append(cls)
return cls
@register_operator
class AddCmd(CalcCmdAbs):
name = "add"
def __init__(self, a, b):
super().__init__(a, b)
self.a = a
self.b = b
def execute(self):
return self.a + self.b
@register_operator
class SubtractCmd(CalcCmdAbs):
name = "subtract"
def __init__(self, a, b):
super().__init__(a, b)
self.a = a
self.b = b
def execute(self):
return self.a - self.b
class NoneCmd(CalcCmdAbs):
name = "None Command"
def execute(self):
print("Invalid Command")
The decorator is the piece that removes the central if chain. Adding a MultiplyCmd means writing one class with @register_operator on top; nothing else changes. NoneCmd is a null object: a command that does nothing harmful, so the caller never has to special-case “unknown operation.”
Looking commands up by name
src/utils.py turns the registry into a dictionary and resolves a name to an instantiated command:
# src/utils.py
from .commands import OPERATORS, NoneCmd
def get_commands() -> dict:
return dict([cmd.name, cmd] for cmd in OPERATORS)
def parse_commands(commands: dict, operation: str, *args):
command = commands.setdefault(operation, NoneCmd)
return command(*args)
parse_commands returns a command object, not a result. That separation is the whole pattern: the code that decides what to do and the code that does it can live in different places and run at different times. The dictionary’s setdefault returns NoneCmd for anything unrecognized, so a typo produces a harmless “Invalid Command” instead of a KeyError.
Testing it
Because commands are plain objects, they are trivial to test. src/tests.py:
# src/tests.py
import unittest
from .utils import get_commands, parse_commands
class TestCase(unittest.TestCase):
def setUp(self) -> None:
self.commands = get_commands()
def test_addition(self):
command = parse_commands(self.commands, "add", 1, 1)
self.assertEqual(command.execute(), 2, "Addition fail")
def test_subtraction(self):
command = parse_commands(self.commands, "subtract", 1, 1)
self.assertEqual(command.execute(), 0, "Subtraction fail")
Run them from the project root with python -m unittest src.tests.
Extending it
Two additions turn this toy into something you would ship.
Undo. Give the base class an undo() method and have each command store enough state to reverse itself. AddCmd.undo() returns self.a, for example. Keep executed commands on a list and pop them to undo.
Arguments by keyword. The constructor already captures **kwargs. Parsing "add a=1 b=2" into keyword arguments lets commands with different signatures share one entry point.
Summary
The command pattern is three small decisions: represent each action as an object with a common interface, keep a registry so actions can be found by name and separate choosing an action from running it. In Python, an abstract base class, a decorator and a dictionary are all you need. For a different take on structuring Python projects, see the pip and Poetry cheat sheet.

