Home โบ Output practice โบ the mutable default argument
Predict the output: the mutable default argument
๐ฎ Predict the outputTopic: functionsDifficulty: hardNo login needed
The snippet
Two mental models collide here. Most learners assume basket=[] is built fresh every call. It is not โ Python evaluates default arguments once, when the def line runs, and every later call shares that one list.
def add(item, basket=[]):
basket.append(item)
return basket
print(add(1))
print(add(2))
print(add(3, []))
print(add(4))
Your prediction
Type exactly what this program prints โ one line per print() call:
The output
[1]
[1, 2]
[3]
[1, 2, 4]
Verified against CPython 3.11 in this repo's build
Line-by-line explanation
def add(item, basket=[]):
The empty list is created now and stored inside the function object asadd.__defaults__. Every call that omits the second argument receives *that same list*.print(add(1))
basket starts as the shared[], 1 is appended, and the function returns it โ[1].print(add(2))
The shared list already holds 1. Appending 2 gives[1, 2]โ the previous call's item is still there, which is exactly what makes this a bug in real code.print(add(3, []))
A list is passed explicitly, so the default is bypassed entirely. This call uses a brand-new list โ[3], and the shared default is untouched (still[1, 2]).print(add(4))
Back to the shared default:[1, 2]+ 4 โ[1, 2, 4]. The[3]from the third call never joined it.
The rule you can reuse
Default arguments are evaluated once at definition time. Use basket=None and then if basket is None: basket = [] inside the body when you want a fresh list per call.
Why most predictions break here
Answering [1] [2] [3] [4]. That is what happens if a new list really were created per call โ the third line (which does get a fresh list) is the control experiment that proves it does not.
Quick fire
Same rule, two more shapes. Predict first, then open.
Predict this too
def f(x=[]):
x.append(1)
return len(x)
print(f(), f(), f())Output1 2 3
The same list is counted three times, so the length grows: 1 2 3.
Predict this too
def f(x=None):
if x is None:
x = []
x.append(1)
return len(x)
print(f(), f(), f())Output1 1 1
The None sentinel builds a new list on every call, so the length never accumulates: 1 1 1.
Share your result
Challenge a friend โ the link contains the question, never the answer:
Copied โKeep practising this rule
More output questions โ All interactive problemsQuestions this page answers
Is this only about lists?
No โ any mutable default is shared: dict, set, custom class instances, or a list inside a tuple. An int, str or tuple default looks safe only because you cannot mutate it in place, so rebinding it hides the sharing.
Does the interview question change if I pass an argument?
Yes, and that is the trick in line 3: passing [] explicitly skips the default, so that call starts clean while the others keep accumulating.