Using default_factory in Python Dataclasses

In Python, the dataclasses module simplifies the creation of classes by automatically generating boilerplate methods like __init__ and __repr__. This article explains the purpose of dataclasses.field with the default_factory parameter, demonstrating how it prevents shared mutable state bugs and allows you to generate dynamic default values for each new class instance.

The Problem: Mutable Default Arguments

In standard Python functions and classes, using mutable objects (such as lists, dictionaries, or sets) as default values leads to unexpected behavior. Because default values are evaluated only once when the class or function is defined, every instance created without an explicit argument shares the exact same mutable object in memory.

To prevent this common bug, Python's @dataclass decorator actively raises a ValueError if you attempt to assign a mutable default directly:

from dataclasses import dataclass

@dataclass
class Team:
    # This raises ValueError: mutable default <class 'list'> for field members is not allowed: use default_factory
    members: list = []

The Solution: dataclasses.field(default_factory=...)

The primary purpose of dataclasses.field with default_factory is to provide a zero-argument callable (a function) that Python executes to produce a fresh default value every time a new instance is instantiated.

Instead of assigning a shared object, you provide a callable such as list, dict, or set:

from dataclasses import dataclass, field

@dataclass
class Team:
    name: str
    members: list = field(default_factory=list)

team_a = Team(name="Engineering")
team_b = Team(name="Marketing")

team_a.members.append("Alice")

print(team_a.members)  # Output: ['Alice']
print(team_b.members)  # Output: []

Because list is passed to default_factory, each Team instance receives its own unique list object.

Generating Dynamic Defaults

Beyond preventing mutable state issues, default_factory is used to compute dynamic default values at the moment of object creation.

For example, if you want a timestamp field to reflect the exact time an instance is created rather than the time the class was imported, pass a function like datetime.now to default_factory:

from dataclasses import dataclass, field
from datetime import datetime
import uuid

@dataclass
class Session:
    session_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    created_at: datetime = field(default_factory=datetime.now)

Key Rules