Batu Lab NotesPractical developer guides

Prevent a mutable default list in a dataclass

By Batu · English technical notes

Also published in our Blogger archive.

dataclasses.field(default_factory=list) calls list separately whenever an instance needs its default. That gives each TodoList an independent empty list. In the example, morning receives one item while evening remains empty; the identity assertion checks that the two attributes are not the same list object.

This matters for per-instance state such as pending tasks, warnings, or collected results. A shared mutable default would let one instance’s mutation become visible through another instance. Current dataclasses reject ordinary unhashable defaults as a guard against this common mistake, but default_factory is the direct, readable way to express the intended ownership.

The factory must be a zero-argument callable. list is appropriate here because it constructs an empty list each time; use another suitable factory when the desired initial value differs. This only separates defaults created by the dataclass. Passing the *same* list explicitly to two constructors would still intentionally share it. Dataclasses were added in Python 3.7. See the official field() documentation.

AI assistance disclosure: this article was drafted with AI assistance and should be adapted to the application’s own data rules.

from dataclasses import dataclass, field


@dataclass
class TodoList:
    items: list[str] = field(default_factory=list)


morning = TodoList()
evening = TodoList()
morning.items.append("write tests")

assert morning.items == ["write tests"]
assert evening.items == []
assert morning.items is not evening.items

print(morning.items)
print(evening.items)
['write tests']
[]