ComparisonDifficulty: Intermediate2 min

Command Classes vs Functions and functools.partial in Python

A function with its arguments bound is a command with execute() and nothing else.

In Python a command that only needs execute can be a function with its arguments already bound. functools.partial takes a function and some arguments and returns a new callable that, when called with nothing, calls the original with them. This card queues three such commands, then sets out what the class form gives that a callable cannot.

py
from functools import partial


def send_email(to: str, subject: str) -> None:
    print(f"email to {to}: {subject}")


def charge(card: str, pence: int) -> None:
    print(f"charged {pence} pence to card ending {card}")


queue = [
    partial(send_email, "[email protected]", "Order received"),
    partial(charge, "4242", 1600),
    lambda: print("stock reserved"),
]

print("queued:", len(queue))
for command in queue:
    command()
OutputRan with python:3.12-slim (Python 3.12.13)
queued: 3
email to [email protected]: Order received
charged 1600 pence to card ending 4242
stock reserved
Two partials and a lambda wait in a list, then run in order.

Each item in queue is a request that has not happened yet: the email, the charge and the stock reservation are values in a list, and nothing runs until the loop calls them. That is a command with an execute called by the name __call__, and no class was written.

The two forms compared
ConcernA command classA function or partial
Run it laterexecute()Call it with ()
UndoA second method, undo, with the state it savedNo second method to call
State saved while runningFields, such as the text a delete removedOnly through a closure, which is hard to read back
Inspect or log itFields and a __repr__partial exposes func, args and keywords; a lambda shows only <lambda>
Serialise to a job queueA class with plain fieldsA partial of a module-level function can be pickled; a lambda cannot

The decision rule follows from the second row. Use a function or a partial when the request only has to run, later or elsewhere: callbacks, deferred jobs and button actions without undo. Use a class as soon as the request must be reversed, because undo needs a second method and the state the first one saved, and a callable has room for neither.