Backend / Python core / Tricky questions / 18_dataclass_mutable_default.md

Dataclass mutable defaults

Updated 1 min read source
On this page5
  1. The gotcha
  2. Minimal repro
  3. Why it happens
  4. Subtler variants
  5. Interview angle

Dataclass mutable defaults

The gotcha

Same root issue as 01_mutable_default_arguments.md, but specific to @dataclass: trying to use field: list = [] raises a ValueError at class construction time. You must use field(default_factory=list).

Minimal repro

python
from dataclasses import dataclass

@dataclass
class Bad:
    items: list = []
# ValueError: mutable default <class 'list'> for field items is not allowed:
# use default_factory

The error message is helpful — Python catches the bug at class-definition time for dataclasses. Without dataclass:

python
class Bad:
    items = []   # silent bug — class variable shared by all instances

a = Bad(); b = Bad()
a.items.append(1)
b.items   # [1]   ← shared

Why it happens

@dataclass validates field defaults. Mutable types (list, dict, set) raise immediately because the framework knows you almost certainly meant “fresh value per instance”. Non-mutable defaults (int, str, None, frozen dataclasses) are fine.

default_factory is a callable invoked once per __init__ call:

python
from dataclasses import dataclass, field

@dataclass
class Good:
    items: list = field(default_factory=list)
    metadata: dict = field(default_factory=dict)
    tags: set = field(default_factory=set)

Subtler variants

Frozen dataclass with mutable nested values

python
@dataclass(frozen=True)
class Config:
    options: list = field(default_factory=list)

c = Config()
c.options.append("x")   # works — frozen blocks attribute *rebinding*, not mutation
c.options = []          # FrozenInstanceError

frozen=True prevents setting attributes, but doesn’t make their values immutable. Use tuple or frozenset if you want true immutability.

Sentinel default

If “no value” is meaningful and None is a valid value, use dataclasses.MISSING sentinel or a custom one:

python
_UNSET = object()

@dataclass
class Settings:
    timeout: object = field(default=_UNSET)

Interview angle

“What’s wrong with @dataclass\nclass C:\n items: list = []?” Then: “How would you fix it?” — field(default_factory=list) is the answer.