Python mutable default pitfall (or feature)
2025-03-02 til python pythonquirk
General mutable default issue
def buggy(items=[]): # Mutable default - DANGEROUS
items.append(1)
return items
print(buggy()) # [1]
print(buggy()) # [1, 1] - Unexpected!
# OUTPUT
# [1]
# [1, 1]
See this deep dive as well Common Gotchas — The Hitchhiker's Guide to Python (archived)
Problem
Classes decorated with dataclass
reject mutable default values (lists, dicts, sets) with error:
field args is not allowed: use default_factory
Cause
Python evaluates default arguments once at definition time, not per-instance. Mutable defaults become shared across all instances, causing side-effect bugs.
Solution
Use field(default_factory=list)
pattern:
from dataclasses import dataclass, field
@dataclass
class Example:
# WRONG: immutable_field: list = [] # Shared across instances!
# CORRECT: Creates new list per instance
mutable_field: list = field(default_factory=list)
dict_field: dict = field(default_factory=dict)
custom_obj: MyClass = field(default_factory=MyClass)
Implementation Note
default_factory
accepts any callable that returns a new instance. For custom types, pass the class itself or a function that instantiates it.
Another example
From Python Cookbook
book, page 223
>>> def spam(a, b=[]):
... print(b)
... return b
>>> x = spam(1)
>>> x
[]
>>> x.append(99)
>>> x.append('Yow!')
>>> x
[99, 'Yow!']
>>> spam(1) # Modified list gets returned!
[99, 'Yow!']
>>>