Dataclasses — `@dataclass` decorator, `__post_init__`
2.1.16· Coding › OOP Fundamentals
WHAT is a dataclass?
from dataclasses import dataclass
@dataclass
class Point:
x: float # field (must be annotated!)
y: float
label: str = "P" # field with defaultYeh single declaration roughly equivalent hai haath se likhne ke:
class Point:
def __init__(self, x: float, y: float, label: str = "P"):
self.x = x
self.y = y
self.label = label
def __repr__(self):
return f"Point(x={self.x!r}, y={self.y!r}, label={self.label!r})"
def __eq__(self, other):
if other.__class__ is not self.__class__:
return NotImplemented
return (self.x, self.y, self.label) == (other.x, other.y, other.label)HOW decorator kaam karta hai (derivation from scratch)
Chalte hain step by step mental model banate hain — Feynman-style — taaki magic gayab ho jaaye.
Step 1 — Fields collect karo. Python cls.__annotations__ padhta hai, ek ordered dict {name: type}. Order = source order. Kyun? __init__ ka parameter order field order se match karna chahiye.
Step 2 — Defaults dhundho. Har annotated name ke liye check karo ki koi class-level value exist karti hai (label = "P"). Agar hai toh woh default hai. Kyun? Taaki __init__(... label="P") generate kiya ja sake.
Step 3 — __init__ generate karo. Ek function string banao: parameters field order mein, defaults attached, body = self.<name> = <name>. Kyun generated function? Runtime pe reflection ke mukable speed + correctness.
Step 4 — __repr__ / __eq__ generate karo jab tak tumne disable na kiya ho (@dataclass(repr=False)). __eq__ fields ka tuple compare karta hai. Kyun tuple? Tuple comparison lexicographic, field-by-field equality free mein deta hai.
__post_init__ — auto-__init__ ke BAAD code run karna
from dataclasses import dataclass, field
@dataclass
class Rectangle:
width: float
height: float
area: float = field(init=False) # NOT a constructor arg; we compute it
def __post_init__(self):
if self.width <= 0 or self.height <= 0:
raise ValueError("dimensions must be positive")
self.area = self.width * self.height
Worked examples
Recall Feynman: 12-year-old ko explain karo
Socho tum har baar naya toy banate waqt wahi form bharte ho: naam, colour, size. @dataclass ek robot stamp hai — tum sirf blanks list karo (naam, colour, size) aur robot poora form likhta hai, plus ek tarika usse zor se padhne ka aur ek tarika check karne ka ki do toys same hain ki nahi. __post_init__ ek "ab double-check karo" step hai: robot form fill karne ke baad, woh tumhari chhoti checklist run karta hai ("size zero nahi ho sakta!") aur extra cheezein compute kar sakta hai ("total price = price × quantity").
Flashcards
What does the @dataclass decorator auto-generate from annotated fields?
__init__, __repr__, aur __eq__ (plus __hash__ agar frozen ho).Why must dataclass fields be type-annotated?
__annotations__ padh ke; ek un-annotated x = 5 sirf ek class attribute hai, field nahi.When is __post_init__ called?
__init__ ke last statement ke roop mein, saare fields assign hone ke baad.Why is items: list = [] an error in a dataclass?
field(default_factory=list) use karo.What does field(init=False) do?
__init__ ke parameters se hata deta hai; typically baad mein __post_init__ mein set hoti hai.Why does frozen=True make a dataclass hashable?
__hash__ generate karta hai. Mutable dataclasses __hash__ = None set karte hain.How do you assign a derived field inside __post_init__ of a frozen dataclass?
object.__setattr__(self, "name", value) freeze bypass karne ke liye.How does the generated __eq__ compare two instances?
What's default_factory and why use it over default?
Connections
- OOP Fundamentals — dataclasses classes ke data-holding pattern ka shortcut hain.
- Dunder Methods __init__ __repr__ __eq__ — woh jo decorator actually generate karta hai.
- Immutability and Hashing —
frozen=Trueaur__hash__. - Default Arguments and Mutable Default Trap — same bug, different syntax.
- NamedTuple vs Dataclass vs TypedDict — kab kaunsa choose karna hai.
- Type Annotations in Python — kyun annotations field detection drive karti hain.