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!']
>>>




Incoming Internal References (0)


Outgoing Web References (0)

Receive my updates

Barış Özmen © 2025