Arguments in Depth
Intermediate · 20 min read · ▶ live playground · ✦ checkpoint
One function can feel easy until the call gets crowded. Python gives you several ways to pass arguments so your calls can stay clear: by position, by name, with defaults, collected into groups, or unpacked from collections.
Choose order or labels
A positional argument is an argument matched by its order in the function call. A keyword argument is an argument written with the parameter name, like day="Friday", so Python matches it by label instead of by order.
def show_booking(player, room, day):
print(f"{player} booked {room} on {day}.")
show_booking("Mina", "studio", "Friday")
show_booking(day="Monday", player="Ravi", room="lab")Mina booked studio on Friday.Ravi booked lab on Monday.
Think back to the recipe-card analogy from 7.1. Positional arguments fill the blanks from left to right. Keyword arguments write the blank's label on the ingredient: day="Friday" says exactly where "Friday" belongs.
Once you use a keyword argument in a call, keep the rest keyword-style too. Python lets positional arguments come first, then keyword arguments. The reverse order is not allowed because Python would have to guess which blank the later positional value should fill.
Give parameters defaults
A default parameter value is a value Python uses when the code calling the function leaves that argument out. Defaults are good when one choice is common but not universal.
def receipt_line(item, quantity=1, urgent=False):
message = f"{quantity} x {item}"
if urgent:
message = message + " (pack today)"
return message
print(receipt_line("tea"))
print(receipt_line("apples", 3))
print(receipt_line("mangoes", 2, urgent=True))1 x tea3 x apples2 x mangoes (pack today)
Defaults make the common call short: receipt_line("tea"). They still let you override the value when the story changes. Put parameters with defaults after the required parameters, so the function call remains readable from left to right.
The mutable default trap
A mutable default argument trap is a bug where a default list, dictionary, or set is created once when def runs, then reused by later calls. If the function changes that default collection, the next call sees the old change. On the recipe card, it is one shared row of slots, not a fresh row each time.
def add_badge(player, badges=[]):
badges.append(player)
return badges
print(add_badge("Mina"))
print(add_badge("Ravi"))
print(add_badge("Noor"))['Mina']['Mina', 'Ravi']['Mina', 'Ravi', 'Noor']
That output is wrong for a function that looks like it should start with a fresh badge list each time. The default list is the same list object across all three calls.
Use None, the reserved empty seat from lesson 2.2, when you mean "no collection was provided."
def add_badge(player, badges=None):
if badges is None:
badges = []
badges.append(player)
return badges
print(add_badge("Mina"))
print(add_badge("Ravi"))
team_badges = ["helper"]
print(add_badge("Noor", team_badges))['Mina']['Ravi']['helper', 'Noor']
The is operator checks whether two names point to the same object; you met object identity with id() back in lesson 2.1, and name lifetime gets a fuller story in 7.3. Here, badges is None means "did this call leave the blank empty?"
Collect extra arguments
Sometimes a call should be allowed to send a flexible number of values. *args is a parameter that collects extra positional arguments into a tuple. **kwargs is a parameter that collects extra keyword arguments into a dictionary.
def total_scores(player, *scores):
total = 0
for score in scores:
total = total + score
return f"{player}: {total}"
print(total_scores("Mina", 10, 20, 5))
print(total_scores("Ravi", 7, 8))
def show_profile(player, **details):
print(player)
for label, value in details.items():
print(f"{label}: {value}")
show_profile("Noor", mood="focused", snack="apples")Mina: 35Ravi: 15Noormood: focusedsnack: apples
The names args and kwargs are conventions, not magic. The stars are the important part. You can write *scores, and that is often clearer when the extra values are scores. The two-star form is useful when a function can accept optional labels without knowing all of them ahead of time.
Require clarity with * and /
A keyword-only parameter is a parameter that must be passed by name. Put it after a * by itself in the def line. A positional-only parameter is a parameter that must be passed by order. Put it before / in the def line.
def plan_snack(item, quantity, *, urgent=False):
message = f"{quantity} x {item}"
if urgent:
message = message + " - today"
return message
def add_bonus(score, /, bonus=10):
return score + bonus
print(plan_snack("apples", 3))
print(plan_snack("tea", 1, urgent=True))
print(add_bonus(42))
print(add_bonus(42, bonus=3))3 x apples1 x tea - today5245
urgent=True reads better than a mystery third positional argument like True. Keyword-only parameters are a small design tool: they make the call explain itself.
You will not write positional-only parameters every day as a beginner. But you will see / in documentation for built-in functions, so it is worth recognizing: names before / are not meant to be used as keyword labels.
Try it - unpack at the call site
A call site is the line where a function is called. Call-site unpacking means using * or ** in that call to open a collection into separate arguments.
Run this, then change the snack_order tuple and snack_options dictionary. The function stays the same; the call pulls its arguments from data you already have.
At the call site, *snack_order opens the tuple into positional arguments: "apples", then 3. **snack_options opens the dictionary into keyword arguments: urgent=True, then note="for Mina".
Use unpacking when the data already has the right shape. Do not use it to make a call clever. A readable direct call is still better when there are only one or two obvious values.
Checkpoint
Answer all three to mark this lesson complete
You now control how values enter a function: by order, by label, by default, by collection, or by unpacking. Next, those values get names inside the function, and Python's scope rules decide exactly how long those names live.