Skip to main content

Dataclasses: Model Data Fast

intermediate13 min readLesson 64 of 169

@dataclass generates __init__, __repr__, and __eq__ so you model, not boilerplate.

Most classes are mostly data. Writing __init__, __repr__, and __eq__ by hand for each one is boilerplate with bug surface. @dataclass generates all three from your field declarations:

from dataclasses import dataclass, field

@dataclass
class Task:
    title: str
    priority: int = 1
    tags: list = field(default_factory=list)   # safe mutable default

task = Task("ship release", 2)
task == Task("ship release", 2)     # True โ€” __eq__ compares fields
repr(task)                          # Task(title='ship release', ...)

Three details professionals know:

  • Mutable defaults need field(default_factory=list) โ€” a bare tags=[] would share one list across all instances (same trap as def f(x=[])).
  • frozen=True makes instances immutable and hashable โ€” great for value objects like Money or Point.
  • __post_init__ runs after __init__ for cross-field validation.

When to use which

Plain class โ†’ behavior-heavy objects with complex lifecycle. Dataclass โ†’ data records: rows, configs, messages, DTOs. Reaching for a dataclass first is a signal you're thinking about data shape, which usually makes better designs than thinking about inheritance hierarchies.

Now practice

Dataclass DrillsModel records with @dataclass โ€” defaults, frozen, and equality.3 challenges ยท ยท ~25 min