Functions Deep Dive: *args, **kwargs, and Defaults
Design function signatures that are flexible, safe, and pleasant to call
*args, and **kwargs — to make one function handle many calling styles without becoming brittle. Used well they remove boilerplate; used carelessly they hide subtle bugs.
- pass arguments by position and by keyword, and know when each is clearer;
- give parameters default values and reason about when the default is used;
- collect any number of extra positional arguments with
*args(a tuple); - collect any number of extra keyword arguments with
**kwargs(a dict); - order parameters correctly and define keyword-only parameters;
- unpack sequences and dicts into a call with
*and**; - avoid the notorious mutable default argument trap.
1. Positional vs keyword arguments
You already call functions. There are two ways to supply each argument. A positional argument is matched to a parameter by its position in the call. A keyword argument is matched by name, written as name=value.
def book_room(guest, nights, breakfast):
print(guest, "-", nights, "nights, breakfast:", breakfast)
# All positional: order decides what goes where
book_room("Mara", 3, True)
# All keyword: order no longer matters, names do
book_room(nights=3, guest="Mara", breakfast=True)
# Mixed: positional first, then keyword
book_room("Mara", breakfast=True, nights=3)
Output:
Mara - 3 nights, breakfast: True
Mara - 3 nights, breakfast: True
Mara - 3 nights, breakfast: True
Keyword arguments make a call self-documenting: book_room("Mara", 3, True) forces the reader to remember what True means, but breakfast=True says it out loud. The one firm rule: once you use a keyword argument, every argument after it must also be a keyword. A positional argument may never follow a keyword argument.
book_room(guest="Mara", 3, True) raises SyntaxError: positional argument follows keyword argument.