What does `@dataclass` give you over a plain class, and what are its main configuration options?
`@dataclass` auto-generates `__init__`, `__repr__`, and `__eq__` from the field annotations declared in the class body, eliminating boilerplate. Key options include `frozen=True` for immutability and automatic `__hash__`, `order=True` for comparison operators, and `slots=True` (Python 3.10+) for memory-efficient slot-based storage.
How to think about it
@dataclass is a decorator that reads your field annotations and writes the dunder boilerplate you’d otherwise type by hand — __init__, __repr__, __eq__. What an interviewer actually wants is for you to say what it generates, which options change that, and the one gotcha everyone hits: a mutable default like tags: list = [] is rejected outright.
A worked example
from dataclasses import dataclass, field
# A plain @dataclass generates __init__, __repr__, and __eq__ for you
@dataclass
class Point:
x: float
y: float
z: float = 0.0
p1, p2, p3 = Point(1.0, 2.0), Point(1.0, 2.0), Point(1.0, 2.0, 3.0)
print("repr:", p1)
print("eq p1==p2:", p1 == p2) # __eq__ compares fields
print("eq p1==p3:", p1 == p3)
# frozen + order add immutability/hashing and the comparison operators
@dataclass(frozen=True, order=True)
class Vector:
x: float
y: float
tags: list = field(default_factory=list, compare=False) # excluded from ==/<
v1, v2 = Vector(1.0, 2.0), Vector(3.0, 0.5)
print("v1 < v2?", v1 < v2) # order=True -> __lt__ etc.
print("hashable?", hash(v1) == hash(Vector(1.0, 2.0))) # frozen=True -> __hash__
print("set of vectors:", sorted({v1, v2})) # so it works in a set
# field() gives per-field control: safe mutable defaults, hidden fields
@dataclass
class ModelConfig:
name: str
lr: float = 0.001
layers: list = field(default_factory=list)
_internal: str = field(default="secret", repr=False, compare=False)
print("config:", ModelConfig("mlp", layers=[128, 64])) # _internal hidden from repr
repr: Point(x=1.0, y=2.0, z=0.0)
eq p1==p2: True
eq p1==p3: False
v1 < v2? True
hashable? True
set of vectors: [Vector(x=1.0, y=2.0, tags=[]), Vector(x=3.0, y=0.5, tags=[])]
config: ModelConfig(name='mlp', lr=0.001, layers=[128, 64])
Three flags carry most of the value. frozen=True makes instances immutable and gives them a __hash__, so they can live in sets and as dict keys. order=True synthesises __lt__ and friends, comparing fields left to right. And field(...) tunes a single field — compare=False keeps tags out of equality and hashing, repr=False hides _internal from the printed form.
slots=True (Python 3.10+)
@dataclass(slots=True)
class Pixel:
x: int
y: int
color: str
This is the same memory win as a hand-written __slots__ — no __dict__, lower per-instance cost, faster attribute access — without typing the slot names yourself. Worth it when you’ll create millions of instances.