The @dataclass decorator (Python 3.7+) automatically generates the boilerplate — __init__, __repr__, __eq__, and more — for classes whose main purpose is holding data.
from dataclasses import dataclass, field
@dataclass
class Point:
x: int
y: int
label: str = "origin" # default value, just like a regular parameter
p1 = Point(3, 4)
p2 = Point(3, 4)
print(p1) # Point(x=3, y=4, label='origin') — __repr__ generated automatically
print(p1 == p2) # True — __eq__ generated automatically, compares field values
@dataclass(frozen=True) # makes instances immutable, like a namedtuple
class ImmutablePoint:
x: int
y: int
@dataclass
class Team:
name: str
members: list[str] = field(default_factory=list) # correct way to default to a mutable type
Without @dataclass, writing an equivalent plain class means manually writing __init__ (assigning every field to self), __repr__ (for readable printing), and __eq__ (for value-based comparison) — @dataclass generates all three automatically purely from the class's type-annotated fields, dramatically reducing boilerplate for simple data-holding classes.
@dataclass(frozen=True) makes instances immutable after creation (attempting to reassign a field raises an error), similar in spirit to a namedtuple but with full type hints and the ability to add regular methods alongside the fields.
For mutable default values like a list, use field(default_factory=list) rather than members: list = [] directly — this avoids exactly the same shared-mutable-default trap covered in the earlier lesson on function default arguments, since @dataclass handles creating a fresh list per instance when a factory function is supplied.