Dataclasses
Generate __init__, __repr__ and __eq__ automatically; defaults, field(), __post_init__, frozen, slots and asdict.
Many classes exist mainly to hold data: a Product with a name and price, a Point with x and y, a User with an id and email. Writing __init__, __repr__ and __eq__ for each one is tedious and error-prone. The dataclasses module (standard library, Python 3.7+) generates them from simple type-annotated field declarations.
Before and after#
The hand-written version:
The dataclass version:
Each annotated class variable (name: str) becomes a field. The decorator writes an __init__ that takes the fields in order, a readable __repr__, and a value-based __eq__. Fields with defaults must come after fields without.
Type annotations are required to declare fields, but — as everywhere in Python — they're not enforced at runtime. Product("Keyboard", "expensive") would be accepted; a type checker like mypy would flag it.
Mutable defaults and field()#
Just like function defaults, a mutable default would be shared between instances, so dataclasses refuse it:
Use field(default_factory=...), which calls the factory for every new instance:
field() options let you hide a field from repr (repr=False), ignore it in comparisons (compare=False), or exclude it from __init__ (init=False). Here created is hidden and ignored in comparisons — otherwise two orders made a microsecond apart would never be equal.
Validation and derived fields: __post_init__#
The generated __init__ calls __post_init__ (if you define it) after setting the fields — the place for validation and computed attributes:
Immutable dataclasses: frozen=True#
Freezing prevents changes after creation, which makes objects safer to share and lets them be dict keys and set members:
Ordering, slots and keyword-only fields#
order=Truegenerates<,<=,>,>=, comparing fields in order like tuples.slots=True(3.10+) uses__slots__: less memory, faster attribute access, and typos likecfg.prot = 1raiseAttributeError.kw_only=True(3.10+) forces keyword arguments — great for config objects with many fields.
Converting to dicts and tuples#
asdict works recursively on nested dataclasses, which makes JSON serialisation easy.
Inheritance#
Dataclasses can inherit from each other; fields are combined in order from base to subclass:
Dataclass, NamedTuple, dict — or Pydantic?#
FastAPI, which you'll meet in the web lesson, uses Pydantic models — they look very similar to dataclasses but validate and convert types at runtime.
Worked example: an inventory#
Common mistakes#
- Forgetting the annotation:
name = "x"without: stris a plain class attribute, not a field. - Mutable defaults — use
field(default_factory=list). - Non-default field after a default one →
TypeError: non-default argument 'b' follows default argument .... Reorder fields or usekw_only=True. - Expecting runtime type checking — dataclasses don't validate types; use
__post_init__or Pydantic.
What's next#
That wraps up object-oriented Python. Next we'll look at how for loops really work under the hood — iterators and generators.
Check your understanding
Quick quiz
1.Which methods does
@dataclassgenerate by default?2.How do you give a dataclass field a default empty list?
3.What does
@dataclass(frozen=True)do?
Finished reading?
Mark this lesson complete to track your progress.