DoesItHold

Why a mutable default argument leaks state in Python

A default argument in Python is evaluated once, when the function is defined, not each time it is called. If that default is a mutable list, every call that omits the argument shares the same list.

The quick answer

Do not use a mutable value like a list as a default argument. Default to None, then create a fresh list inside the function when no argument is supplied.

A minimal example

Bugged: the default list is created once and reused
def record(label, history=[]):
    history.append(label)
    return history
Fixed: a new list is created per call
def record(label, history=None):
    if history is None:
        history = []
    history.append(label)
    return history

In the bugged version, calling the function twice without the optional argument appends to the same list, so results accumulate across calls. The None guard gives each call its own list.

How to debug it

  1. Call the function twice in a row without passing the optional argument.
  2. If the second result contains data from the first call, the default is shared.
  3. Look for a mutable default: a list, dict, or set in the signature.
  4. Replace it with None and build the container inside the function.

Common wrong fixes

  • Clearing the list at the start of the function, which mutates a shared object others may hold.
  • Copying the default inside the function but keeping the mutable default in the signature.
  • Using a tuple default and then trying to append to it.

Limits of this guide

This is the classic mutable-default case. Related traps include mutable class attributes and shared caches. The challenge focuses on the default-argument version so the cause is unambiguous.

Authoritative references

  1. Python Programming FAQ — shared default values
  2. Python tutorial — default argument values
  3. Python language reference — function definitions

Ready to try it?

Fix this bug in a free challenge

Related pages