BugHunt

Mutable default argument keeps old values

A function with a default list or dict remembers items between calls. The default is created once, when the function is defined — not on every call.

PythonMutation and copying bugs

What it means

def f(items=[]) evaluates [] exactly once and reuses that same list object every time the default is needed. Appending to it mutates the shared default, so the second call sees what the first call added. No error is raised, which is what makes it so hard to spot: the function is simply wrong on its second call.

Common causes

1. A default list that gets appended to

Each call without an argument shares — and grows — the same list.

Breaks

def add_tag(tag, tags=[]):
    tags.append(tag)
    return tags

add_tag("a")   # ['a']
add_tag("b")   # ['a', 'b']  <- surprise

Works

def add_tag(tag, tags=None):
    if tags is None:
        tags = []
    tags.append(tag)
    return tags

2. A default dict used as a cache by accident

Same mechanism with a dict: data from one call leaks into the next.

Breaks

def count(words, seen={}):
    for w in words:
        seen[w] = seen.get(w, 0) + 1
    return seen

Works

def count(words, seen=None):
    seen = {} if seen is None else seen
    for w in words:
        seen[w] = seen.get(w, 0) + 1
    return seen

3. A list default in a dataclass

dataclasses catch this one and raise ValueError: mutable default <class 'list'> for field ... is not allowed: use default_factory.

Breaks

@dataclass
class Order:
    items: list = []

Works

from dataclasses import dataclass, field

@dataclass
class Order:
    items: list = field(default_factory=list)

How to find it in your own code

Use None as the default for anything mutable and create the fresh object inside the function. Linters flag this (flake8-bugbear's B006, pylint's dangerous-default-value) — turning that rule on stops it ever reaching production.

Still not sure why yours breaks?

Paste it into the visualizer and watch it run line by line, with every variable at every step. Free, and it runs in your browser.

Other common errors