backend / python core / tricky questions / 01_mutable_default_arguments.md

Mutable default arguments

1 min read source

Mutable default arguments

The gotcha

Default argument values are evaluated once, at function definition time, and the same object is reused across calls.

Minimal repro

def append_to(item, target=[]):
    target.append(item)
    return target

print(append_to(1))  # [1]
print(append_to(2))  # [1, 2]   ← surprise: same list reused
print(append_to(3))  # [1, 2, 3]

Why it happens

When Python compiles def, it evaluates [] once and stores the resulting list object in func.__defaults__. Every call that omits target binds the parameter to that same shared object. Mutating it via append mutates the default itself.

print(append_to.__defaults__)  # ([1, 2, 3],)

How to avoid

Use None as the sentinel and create the mutable object inside:

def append_to(item, target=None):
    if target is None:
        target = []
    target.append(item)
    return target

For dataclasses, use field(default_factory=list) — same problem in a different shape (see 18_dataclass_mutable_default.md).

Interview angle

Asked to spot the bug, predict output, or fix it. Sometimes hidden inside a longer snippet using def f(x, cache={}) for memoization — which actually works as a cheap memoization trick, but is fragile. Prefer functools.lru_cache.