The mutable default trap and the None fix
The first function shares one list between calls. The second starts fresh each time. Look at the stored defaults at the end of each section.
Python can match call arguments to parameters by position, by name (keyword arguments), or fill in missing ones from defaults. Defaults are evaluated once when the function is defined, which makes a mutable default such as [] a classic trap.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 6 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write net_price(price, discount=0.0, tax=0.18) that returns price * (1 - discount) * (1 + tax) rounded to 2 places. Print net_price(1000), then the price with a 10% discount (name the argument), then with no tax, then net_price(tax=0.05, price=200) to show that keyword order is free.
Write add_tag(tag, tags=None) that appends tag to the list and returns it, using the None fix. Call it twice and show that the second result does not contain the first tag.
Write shout(text, times=1, mark="!") that returns the text in capitals followed by the mark repeated times times. Call it with only text, then with times=3.
Explain it without notes
Explain the mutable default argument trap and how to avoid it.
Which orderings of positional and keyword arguments are allowed in a call, and why?
Why must parameters with defaults come after parameters without?
When would you prefer keyword arguments over positional ones in a call?
Expected output
['apple']
['apple', 'pear']
stored default: (['apple', 'pear'],)
['apple']
['pear']
stored default: (None,)