LearningPython TutorialInheritance & Python Data Model
CHAPTER 17 · OOP DESIGN

Python Inheritance & Data Model

Inheritance existing class ko extend karne ka tool hai, while Python data model special methods ke through objects ko built-in language behavior ke saath integrate karta hai. Is chapter me inheritance ko responsibly use karna, polymorphism samajhna aur Pythonic objects design karna seekhenge.

English + Hinglish74 min readOnline practice included

What is inheritance?

Inheritance me one class another class ke attributes and methods inherit kar sakti hai. Parent/base class common behavior define karti hai, and child/subclass us behavior ko reuse ya extend kar sakti hai.

basic-inheritance.pyPython
class User:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return f"User: {self.name}"


class Student(User):
    pass

student = Student("Aman")
print(student.describe())
Hinglish Explanation

Student(User) ka meaning hai Student, User se inherit karta hai. Student ne describe() khud define nahi kiya, phir bhi inherited method use kar sakta hai.

Try basic inheritance →

Use inheritance for a real is-a relationship

Inheritance tab useful hota hai jab subclass genuinely parent ka specialized version ho. Example: Student is a User. Lekin Car is an Engine nahi hai; Car has an Engine, so composition better fit hai.

Design rule: Code reuse alone inheritance choose karne ka enough reason nahi. Meaningful relationship and substitutability important hain.

Call parent behavior with super()

super.pyPython
class User:
    def __init__(self, name):
        self.name = name


class Student(User):
    def __init__(self, name, course):
        super().__init__(name)
        self.course = course

student = Student("Riya", "Python")
print(student.name, student.course)

super() parent class ko hard-code kiye bina next implementation ko access karta hai. Multiple inheritance me ye method resolution order ke saath cooperate karta hai.

Method overriding

Subclass inherited method ko same name ke saath redefine kar sakti hai.

override.pyPython
class User:
    def role(self):
        return "user"


class Student(User):
    def role(self):
        return "student"

print(User().role())
print(Student().role())

Method override karte waqt caller ke expected contract ko preserve karna important hai.

Extend instead of fully replacing

extend.pyPython
class User:
    def summary(self):
        return "BrounStack user"


class Student(User):
    def summary(self):
        base = super().summary()
        return f"{base} · student"

print(Student().summary())

Child method parent result ko use karke additional behavior add kar sakta hai.

Polymorphism

Polymorphism means same operation different object types par meaningful behavior produce kar sakti hai.

polymorphism.pyPython
class EmailNotification:
    def send(self):
        return "Email sent"


class SmsNotification:
    def send(self):
        return "SMS sent"


def notify(channel):
    print(channel.send())

notify(EmailNotification())
notify(SmsNotification())

Notice: EmailNotification and SmsNotification ko common parent class ki zaroorat nahi. Python me compatible behavior often enough hota hai.

Try polymorphism →

Duck typing

Python frequently object ke exact class se zyada uske supported operations par focus karta hai. Agar object required method/protocol support karta hai, code use kar sakta hai.

Think: “Can this object do what I need?” instead of always “Is this object exactly this class?”

isinstance() and issubclass()

checks.pyPython
class User:
    pass

class Student(User):
    pass

student = Student()
print(isinstance(student, Student))
print(isinstance(student, User))
print(issubclass(Student, User))

Runtime type checks kabhi useful hain, but excessive isinstance() chains polymorphic design ko defeat kar sakte hain.

Multiple inheritance — beginner view

Python class multiple base classes se inherit kar sakti hai. Powerful hai, but design complexity increase hoti hai.

multiple.pyPython
class Printable:
    def print_info(self):
        return "Printable"

class Savable:
    def save(self):
        return "Saved"

class Report(Printable, Savable):
    pass

report = Report()
print(report.print_info())
print(report.save())

Small mixin-like behaviors useful ho sakte hain, but deep/complicated inheritance trees beginner projects me avoid karo.

Method Resolution Order (MRO)

Multiple inheritance me same method multiple bases me ho sakta hai. Python deterministic MRO use karta hai to decide lookup order.

mro.pyPython
class A:
    def show(self):
        return "A"

class B(A):
    pass

class C(A):
    def show(self):
        return "C"

class D(B, C):
    pass

print(D().show())
print([cls.__name__ for cls in D.mro()])

super() simply “my parent” nahi; it follows the MRO. Isi reason se cooperative inheritance me consistent super() usage important hota hai.

What is the Python data model?

Python data model defines how objects participate in language operations. Special methods, often “dunder” methods, built-in syntax aur functions ke hooks provide karte hain.

  • __str__ — human-friendly string.
  • __repr__ — developer/debug representation.
  • __len__len(obj).
  • __eq__obj1 == obj2.
  • __lt__ — less-than comparison.
  • __add__obj1 + obj2.
  • __iter__ — iteration protocol; Chapter 18 me detail.

__str__ and __repr__

representations.pyPython
class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __str__(self):
        return f"{self.name}: {self.score}"

    def __repr__(self):
        return f"Student(name={self.name!r}, score={self.score!r})"

student = Student("Aman", 88)
print(student)
print(repr(student))

__repr__ ideally unambiguous/debug-friendly hona chahiye. !r nested values ka repr use karta hai.

Make an object work with len()

len.pyPython
class Course:
    def __init__(self, students):
        self.students = list(students)

    def __len__(self):
        return len(self.students)

course = Course(["Aman", "Riya", "Kabir"])
print(len(course))

Special method tab add karo jab operation object ke domain me naturally meaningful ho.

Custom equality with __eq__

equality.pyPython
class Student:
    def __init__(self, student_id, name):
        self.student_id = student_id
        self.name = name

    def __eq__(self, other):
        if not isinstance(other, Student):
            return NotImplemented
        return self.student_id == other.student_id

print(Student(1, "Aman") == Student(1, "Aman Verma"))

Unsupported type ke comparison me NotImplemented return karna Python ko reflected/alternative comparison handling ka chance deta hai.

Operator overloading

add.pyPython
class Score:
    def __init__(self, value):
        self.value = value

    def __add__(self, other):
        if not isinstance(other, Score):
            return NotImplemented
        return Score(self.value + other.value)

    def __repr__(self):
        return f"Score({self.value})"

print(Score(40) + Score(35))

Operators ko surprising meaning mat do. + tab overload karo jab addition/combination domain me natural ho.

Equality and hashing caution

Hashable objects sets/dict keys me use ho sakte hain. Custom mutable objects ke liye __eq__ aur __hash__ relationship carefully design karna padta hai. Beginner rule: mutable identity/value objects ko dict key banane ki rush mat karo.

Properties still work with inheritance

Chapter 16 ki properties subclasses me inherit hoti hain. Validation behavior reuse ho sakta hai, but subclass ko parent invariants silently break nahi karne chahiye.

Dataclasses reduce boilerplate

@dataclass data-focused classes ke liye generated __init__, __repr__ and equality behavior provide kar sakta hai.

dataclass.pyPython
from dataclasses import dataclass

@dataclass
class Student:
    name: str
    score: float

first = Student("Aman", 88)
second = Student("Aman", 88)

print(first)
print(first == second)

Type annotations metadata/tooling ko help karti hain; normal Python runtime automatically every assignment ko enforce nahi karta.

Try a dataclass →

Frozen dataclasses

frozen.pyPython
from dataclasses import dataclass

@dataclass(frozen=True)
class CourseCode:
    value: str

code = CourseCode("PY101")
print(code)

frozen=True normal field reassignment ko block karta hai and immutable-style value objects me useful ho sakta hai. Ye deep immutability ka universal guarantee nahi.

Inheritance vs composition

  • Use inheritance for a stable, meaningful is-a relationship.
  • Use composition for has-a relationships and swappable collaborators.
  • Prefer simple designs; deep inheritance trees understand/test karna harder hote hain.
  • Polymorphism ko exact class hierarchy se unnecessarily couple mat karo.

Abstract base classes — preview

abc module explicit interfaces/contracts define karne me help kar sakta hai, but beginner projects me duck typing often sufficient hota hai.

abc-preview.pyPython
from abc import ABC, abstractmethod

class Exporter(ABC):
    @abstractmethod
    def export(self):
        pass

Common beginner mistakes

  • Sirf code reuse ke liye inheritance force karna.
  • Deep inheritance hierarchy banana without clear domain reason.
  • Subclass me parent initializer required hote hue super().__init__() forget karna.
  • Override method ka expected behavior/return contract completely break karna.
  • super() ko always direct parent call samajhna.
  • Multiple inheritance use karke MRO ignore karna.
  • Every helper ko magic/dunder method banana.
  • __repr__ me secrets or sensitive data print karna.
  • __eq__ implement karke unsupported types ko incorrectly compare karna.
  • Dataclass ko business logic/design ka replacement samajhna.

Beginner best practices

  • Inheritance hierarchy shallow rakho.
  • Composition ko strong alternative ke roop me consider karo.
  • super() consistently use karo in cooperative class designs.
  • Special methods tab implement karo jab built-in operation naturally meaningful ho.
  • Readable __repr__ debugging improve karta hai.
  • Domain identity/value semantics decide karke hi custom equality define karo.
  • Data-focused records ke liye dataclass consider karo.
  • Public interface simple and predictable rakho.

Chapter checklist

  • Base class aur subclass ka relationship clear hai?
  • super() se parent/cooperative behavior call kar sakte ho?
  • Method overriding samajh aaya?
  • Polymorphism aur duck typing ka difference/use clear hai?
  • isinstance(), issubclass() aur MRO ka basic idea hai?
  • __str__, __repr__, __len__, __eq__ ka role clear hai?
  • Inheritance vs composition choose karne ka basic rule samajh aaya?
  • @dataclass ka purpose samajh aaya?

Practice Task — Learning Membership System

BrounStack Playground me inheritance + data model based mini system banao.

  1. User base class banao with name and email.
  2. Student subclass banao and course add karo.
  3. Instructor subclass banao and subject add karo.
  4. Dono subclasses me role() override karo.
  5. Constructors me super().__init__() use karo.
  6. List me mixed Student/Instructor objects store karo.
  7. Loop me same role() call karke polymorphism show karo.
  8. __str__ readable output ke liye implement karo.
  9. __repr__ debugging representation add karo.
  10. Student objects ke liye sensible equality rule decide karo.
  11. One small composed object add karo, e.g. Profile or Address.
  12. isinstance() ka one useful check add karo.
  13. One small dataclass value object banao, e.g. CourseCode.
  14. Explain in comments ki kaha inheritance use ki aur kaha composition.

Open practice starter →