Dataclasses
- Replace boilerplate
__init__with@dataclass - Add default values and default factories to dataclass fields
- Get free
__repr__,__eq__, and comparison methods - Choose between
frozen=True(immutable) and mutable dataclasses
FastAPI request models, Pydantic v1 internals, and half of modern config layers use @dataclass or its stricter cousin attrs to kill boilerplate. Staff engineers reach for frozen=True + slots=True when they need hashable, memory-lean value objects for caching keys or event payloads.
- Using a mutable default like
field=[]— raisesValueError; usefield(default_factory=list)instead. - Assuming
frozen=Truemeans deeply immutable — nested lists still mutate; wrap in tuples or useMappingProxyType. - Skipping
slots=Trueon hot-path classes — costs ~40% more memory per instance versus a slotted dataclass.
@dataclass auto-generates __init__, __repr__, and __eq__ from your annotated fields. Modern Python's answer to "a class that just holds data."
Two things to remember
- Use
field(default_factory=list)for mutable defaults (never= []!). - Add
frozen=Trueto make instances immutable (hashable, dict-key safe).
Versus namedtuple / dict
- More readable than dicts (you get attributes, not string lookups)
- More flexible than namedtuple (mutable by default, supports inheritance)
- Less ceremony than a hand-written class
Try it
- Make
Taskfrozen and put one in aset. - Add a
due: datetime | None = Nonefield.
3 graded exercises
Write the code, click Check. We’ll tell you exactly what to fix.
- Exercise 1
Use
@dataclassto definePointwith twofloatfieldsxandy. CreatePoint(3, 4), print it — the auto-generated repr should includePoint(x=3, y=4). - Exercise 2
Define a
Taskdataclass withtitle: str,priority: int = 1, anddone: bool = False. Add a methodmark_done(self)that setsdoneto True and returnsself. Then createTask("Ship"), call.mark_done(), print it — expected repr includesdone=True. - Exercise 3
Define a
Teamdataclass withname: strandmembers: list[str]. Usefield(default_factory=list)formembersso every instance gets its own empty list (never a shared default). Create twoTeams, append to one — the other must stay empty. Print both.