Using a mutable or stateful value as a default for a dataclass attribute is rarely what developers intend. The value is evaluated only once, when the class is defined, so every instance shares the same object.
Default values for dataclass attributes are evaluated once when the class is defined, not each time an instance is created. This is a fundamental aspect of how Python evaluates default values.
When the default is produced by calling a function such as datetime.now() or uuid.uuid4(), or by a mutable container like
[] or list(), the value is computed once and reused across every instance of the dataclass. Every instance therefore ends up
with the same object.
@dataclass
class Event:
timestamp: datetime = datetime.now() # Noncompliant
tags: list = [] # Noncompliant
event1 = Event()
event2 = Event()
event1.tags.append("x")
# event2.tags is also ["x"] — tags is the same list object.
# event1.timestamp and event2.timestamp are both the class-definition time.
In this example, event1 and event2 share the same tags list and the same timestamp.
Using such defaults can cause reliability issues:
Use field(default_factory=…) instead. The default_factory parameter accepts a callable, without parentheses, that is
invoked each time a new instance is created, so each instance receives a fresh value.
The default_factory expects a callable with no arguments. If the function requires arguments, wrap it in a lambda or use
functools.partial.
@dataclass
class Event:
timestamp: datetime = datetime.now() # Noncompliant
event_id: uuid.UUID = uuid.uuid4() # Noncompliant
tags: list = [] # Noncompliant
from dataclasses import dataclass, field
@dataclass
class Event:
timestamp: datetime = field(default_factory=datetime.now)
event_id: uuid.UUID = field(default_factory=uuid.uuid4)
tags: list = field(default_factory=list)