Legends ofPythos
Claim your name
Writing Real Programs

Default and keyword arguments

Lesson 3 of 16

Watch the lesson1:11 · with Sigrid
Mental model: Think of default arguments like a restaurant menu with 'recommended' dishes marked. You can order exactly what you want (override), or let the chef pick for you (use defaults). Keyword arguments are like speaking to the waiter: "I'd like the salmon, and extra lemon"—you name each item explicitly so nothing gets mixed up.
Why it matters: Real programs rarely need every caller to supply identical data. Config files often have sensible fallbacks; invoice generators default tax rates unless overridden; game saves use standard settings until the player customizes them. Defaults and keywords keep your call sites short, readable, and flexible.

Default values

def greet(name, punctuation='!'):
    return f"Hello, {name}{punctuation}"

print(greet('Ada'))        # Hello, Ada!
print(greet('Grace', '.')) # Hello, Grace.
punctuation defaults to !, but you can override it.
A default value is assigned right in the function signature. If a caller does not pass an argument for that parameter, Python uses the default instead. Defaults must come after any required parameters because positional arguments are matched by order.

Keyword arguments

def build_profile(name, age=0, city='Unknown'):
    return {'name': name, 'age': age, 'city': city}

# Positional: order matters
p1 = build_profile('Lin', 34)

# Keyword: names matter, order does not
p2 = build_profile(name='Mira', city='Oslo')
p3 = build_profile(city='Lima', name='Ana', age=28)
Keyword arguments let you skip defaults and change the call order.
When a caller writes city='Oslo', it is passing by keyword. You can mix positional and keyword calls, but in a call every positional argument must come before any keyword argument: build_profile(name='Mira', 34) is a SyntaxError. This keeps Python unambiguous about which value maps to which parameter.
# BAD: the default list is created once, when the function is defined
def add_item(items=[], item='x'):
    items.append(item)
    return items

print(add_item())   # ['x']
print(add_item())   # ['x', 'x'] -- the same list came back!
print(add_item())   # ['x', 'x', 'x']
The same default list is reused by every call.
# GOOD: None means "nothing was passed", so build a fresh list inside
def add_item(items=None, item='x'):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item())   # ['x']
print(add_item())   # ['x'] -- a brand new list every time
A None default lets you build a fresh list on every call.

Your turn

0 of 3 solved

Exercise 1

+30 XP
Write a function make_burger(patty='beef', sauce=None) that returns a dict with keys 'patty' and 'sauce'. If sauce is not provided, it should be the string 'ketchup'. Then call it once with no arguments and print the result.
Write your Python here.

Run your code to check it against the tests.

Exercise 2

+30 XP
Write a function format_invoice(customer='Guest', total=0.0, tax_rate=0) that returns an f-string: 'Invoice for <customer>: $<total> (tax rate <tax_rate>% )'. Call it using keyword arguments only so that customer is 'Ada' and tax_rate is 15, leaving total at its default. Print the result.
Write your Python here.

Run your code to check it against the tests.

Exercise 3

+30 XP
A colleague wrote this function with a mutable list default:
def add_score(scores=[], score=1):
    scores.append(score)
    return scores
Show why it is dangerous by calling add_score() three times and printing each result. Then write your own corrected version called safe_add_score that uses a None default, calls it the same way (three separate no-arg calls), and prints each result so every call starts from an empty list.
Write your Python here.

Run your code to check it against the tests.