2.1.16 · HinglishOOP Fundamentals

Dataclasses — `@dataclass` decorator, `__post_init__`

1,645 words7 min readRead in English

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 default

Yeh 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
Figure — Dataclasses — `@dataclass` decorator, `__post_init__`

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?
Decorator fields dhundta hai __annotations__ padh ke; ek un-annotated x = 5 sirf ek class attribute hai, field nahi.
When is __post_init__ called?
Auto-generated __init__ ke last statement ke roop mein, saare fields assign hone ke baad.
Why is items: list = [] an error in a dataclass?
Ek mutable list saare instances mein shared ho jaati; Python ValueError raise karta hai. field(default_factory=list) use karo.
What does field(init=False) do?
Field ko __init__ ke parameters se hata deta hai; typically baad mein __post_init__ mein set hoti hai.
Why does frozen=True make a dataclass hashable?
Fields creation ke baad change nahi ho sakte, isliye hash stable rehta hai; Python __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?
Woh saare fields ka tuple field-by-field compare karta hai (aur same class require karta hai).
What's default_factory and why use it over default?
Ek zero-arg callable jo mutable defaults ke liye har instance pe ek baar invoke hoti hai, har object ko apna object deti hai.

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 Hashingfrozen=True aur __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.

Concept Map

eliminates

reads

expressed via

drives generation of

includes

includes

includes

class value becomes

used by

shared list causes

fixed by

fresh per instance in

Boilerplate init repr eq

@dataclass decorator

Type-annotated fields

Declare what data you hold

Generated dunders

__init__

__repr__

__eq__

Field defaults

Mutable default trap

field(default_factory=list)