"Python Mutable Default Argument: The Bug and the Fix"
"The Python mutable default argument bug makes your function return yesterday's data. See the exact broken output, why defaults are evaluated once at `def` time, and the two-line None-sentinel fix — plus the dataclass version that raises ValueError."
Last updated: 2026-10-05 You call add_item("banana") and get back ['apple', 'banana'] . You never added an apple. That's the Python mutable default argument bug — your function is remembering the last call — and the fix is two lines. Here's the broken output, why it happens, and the fix, with nothing else in the way. What the Python mutable default argument bug looks like def add_item(item, cart=[]): cart.append(item) return cart print(add_item("apple")) # ['apple'] — looks fine print(add_item("banana")) # ['apple', 'banana'] — WRONG. Where did 'apple' come from? print(add_item("cherry")) # ['apple', 'banana', 'cherry'] — it keeps growing first = add_item("x") second = add_item("y") print(first is second) # True — they're literally the same list object Expected: each call returns a fresh list with just its own item. Actual: every call that omits cart shares one list, and it accumulates across calls for the lifetime of the process. The first call always looks correct — that's why this bug ships to production and hides for months. It only surfaces on the second call in the same process, which is exactly why test suites that always pass an explicit argument never catch it. Why it happens Python evaluates default argument values once, when the def statement runs — not on every call. That [] is created a single time at import/definition, attached to the function object itself (inspect it: add_item.__defaults__ ), and handed to every call that skips the argument. Appending to it mutates the shared default in place. This only bites with mutable defaults — lists, dicts, sets. Immutable defaults ( 0 , "" , None , tuples) are safe: you can't mutate them in place, so…