Card 06/ 09
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.
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()queued: 3
email to [email protected]: Order received
charged 1600 pence to card ending 4242
stock reservedEach 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.
| Concern | A command class | A function or partial |
|---|---|---|
| Run it later | execute() | Call it with () |
| Undo | A second method, undo, with the state it saved | No second method to call |
| State saved while running | Fields, such as the text a delete removed | Only through a closure, which is hard to read back |
| Inspect or log it | Fields and a __repr__ | partial exposes func, args and keywords; a lambda shows only <lambda> |
| Serialise to a job queue | A class with plain fields | A 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.
← → move through sections and cards