Mutable Default Argument
A default argument value is created once, when the function is defined — using a mutable object as one silently shares state across every call that relies on it.
Purpose
Python evaluates a parameter’s default value exactly once — at the moment def runs, not on
every call. For an immutable default (0, "", None) this is invisible. For a mutable one
([], {}, set()) it means every call that doesn’t pass its own value shares and mutates the
same object.
Example
# Broken: the default list is created once and shared by every call.
def add_item_broken(item, basket=[]):
basket.append(item)
return basket
add_item_broken("apple")
add_item_broken("banana") # "apple" is still in there
add_item_broken("apple") -> ['apple']
add_item_broken("banana") -> ['apple', 'banana'] # surprise
# Fixed: use None as the default, and create a fresh list inside the function.
def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
Behavior notes
- The trap applies to any mutable default:
[],{},set(), or an instance of a mutable custom class. It does not apply to immutable defaults (None,0,"", atuple) — there’s nothing to mutate, so nothing to share unexpectedly. - The fix shown above (
basket=None, thenif basket is None: basket = []) is the standard idiom — it defers creating the mutable object to call time, inside the function body, where a fresh one is made on every call that needs one. - Occasionally the sharing is intentional — a cache or memo dict that’s meant to persist across calls is a legitimate, if uncommon, use of this exact behavior. That’s rare enough that it’s worth a comment explaining the intent when it’s genuinely what’s wanted.
Common mistakes
- Writing
def f(items=[]):out of habit, without realizing the empty list is created once, not per call — this is by far the most common way beginners (and not only beginners) hit this trap. - “Fixing” it by clearing the list inside the function (
basket.clear()) instead of creating a new one — this still shares one object across every caller that holds a reference to a previous call’s result, which usually isn’t what’s wanted either.
Implementation notes
Default-argument evaluation timing is defined by the language itself (see the official FAQ entry linked above) — identical, and identically surprising the first time, across every conforming Python implementation.