The one idea: A default like an empty list is created once, when the function is first defined, and then shared by every call — so the function quietly remembers past calls, and your metrics double every time you re-run the cell. You can spot it in one glance at the def line, and the correct fix uses None instead.
⚠️ The trap: Re-running the same cell over the same data just recomputes the same numbers. That feels right and it's wrong. When a helper has a=[](empty-list) or={}(empty-dictionary) default, that container is built once and reused by every call, so each re-run appends to it again and your totals silently double — and they look perfect after a kernel restart, which is exactly why it slips through.
This is one specific Python landmine: a function that gives different answers each time you call it with the same inputs. In notebooks it has a signature crime — metrics that double every time you re-run a cell. You'll learn to spot it in one glance at a def line.
The bug in its purest form
def collect(value, results=[]):
results.append(value)
return results
collect(10) # [10]
collect(20) # [10, 20] ← where did the 10 come from?
The second call never mentioned 10, yet there it is. Here's the mechanism, and it's the whole lesson:
A default value is evaluated once — when the def statement runs — not once per call. That [] created one single list at definition time, and every call that omits results gets that same list. The first call appends 10 to it. The second call…
Keep reading: Mutable Default Arguments
Unlock the full Python Coding track — every lesson, the AI tutor, and full mock assessments.
- Full lesson content
- AI tutor for this section
- Practice questions