Mastery
Mastery/Python/D. Classes & OOP internals
T1 · high-leverage
Compare with Java: Boilerplate-free value classes with generated equals/hashCode

Dataclasses: field(default_factory=...), __post_init__, frozen=True, generated __eq__

@dataclass auto-generates __init__, __repr__, and __eq__ from the declared fields. The mutable default argument trap (topic 02) still applies to dataclass fields -- field(default_factory=list) is the sanctioned fix, since a bare items: list = [] is a TypeError at class-definition time (dataclasses detect and reject it outright, rather than letting the shared-mutable-default bug happen silently).

python
from dataclasses import dataclass, field

@dataclass
class Bucket:
    items: list = field(default_factory=list)

b1 = Bucket()
b2 = Bucket()
b1.items.append(1)
print("independent lists, not shared:", b1.items, b2.items)

__post_init__ runs right after the generated __init__ -- the idiomatic place for computed/derived fields:

python
@dataclass
class Rect:
    w: float
    h: float
    area: float = field(init=False)   # not a constructor parameter
    def __post_init__(self):
        self.area = self.w * self.h

r = Rect(3, 4)
print("computed field:", r.area)

frozen=True makes instances immutable after construction (raises on any attribute assignment), and the generated __eq__ compares field values -- but ordering (<, >) is NOT generated unless you ask for it:

python
@dataclass(frozen=True)
class Point:
    x: int
    y: int

p = Point(1, 2)
try:
    p.x = 99
except Exception as e:
    print(f"frozen dataclass assignment: {type(e).__name__}: {e}")

print("generated __eq__ compares values:", Point(1, 2) == Point(1, 2))

try:
    Point(1, 2) < Point(3, 4)
except TypeError as e:
    print("no ordering without order=True:", e)

Interview angle

A good "how current is your Python" check, similar to Java records: candidates who know field(default_factory=...) exists (and why — the mutable-default trap still applies to dataclass fields) show they've actually used dataclasses in real code, not just read the one-line pitch. Asking "what does frozen=True cost you" is a nice follow-up that tests whether they understand the mutability tradeoff, not just the syntax.

In the industry

Dataclasses are now the default choice for internal data-holding classes in modern Python codebases — DTOs, config objects, structured return values — specifically to avoid hand-writing __init__/__repr__/__eq__ boilerplate and the bugs that come with keeping them in sync by hand. Teams that need genuine immutability or validation reach for pydantic or attrs instead, but the base mental model (declared fields, generated dunders) is the same one dataclasses established as standard.