Lesson 23 of 30 � 26 min � Intermediate

Inheritance & Polymorphism

Inheritance se existing class ko extend karte hain bina duplicate code likhe. Polymorphism ek interface se multiple behaviors support karta hai.

Prerequisite: OOP (Classes & Objects)

Kyun zaroori hai OOP Advanced?

Basic OOP mein tumne classes aur objects banana seekha. Lekin real-world projects mein ek hi jaisa code baar baar likhna padta hai � jaise har jagah name aur age store karna. Inheritance se tum ek baar code likho aur usse extend karo. Polymorphism se ek hi function se alag-alag objects kaam kar sakein.

Real-world analogy: Inheritance jaise bachche parents se gunn lete hain. Polymorphism jaise ek button alag devices par alag kaam karta hai � phone par call, laptop par power off.

Inheritance: parent se child tak

Jab tum ek class ko doosri class se inherit karte ho, child class ko parent ke sab properties aur methods mil jaate hain. Child unhe override bhi kar sakta hai.

inheritance_basics.py
class Animal:
 def __init__(self, name):
 self.name = name
 def speak(self):
 return "..."

class Dog(Animal):
 def speak(self):
 return "Bark!"

class Cat(Animal):
 def speak(self):
 return "Meow!"

dog = Dog("Tommy")
cat = Cat("Kitty")
print(dog.name) # Tommy (parent ka __init__ use hua)
print(dog.speak()) # Bark! (child ne override kiya)
print(cat.speak()) # Meow!

Yahan Dog aur Cat dono Animal se inherit karte hain. __init__ parent ka use hota hai, lekin speak() child ne apne hisaab se badla � isse kehte hain method overriding.

Inheritance ke types

inheritance_types.py
# Single Inheritance
class Vehicle:
 def __init__(self, brand):
 self.brand = brand
 def info(self):
 return f"Vehicle: {self.brand}"

class Car(Vehicle):
 def __init__(self, brand, doors):
 super().__init__(brand) # parent ka __init__
 self.doors = doors

car = Car("Toyota", 4)
print(car.info()) # Vehicle: Toyota
print(car.doors) # 4

# Multiple Inheritance
class Flyable:
 def fly(self):
 return f"{self.name} can fly!"

class Bird(Animal, Flyable):
 def speak(self):
 return "Tweet!"

bird = Bird("Eagle")
print(bird.speak()) # Tweet!
print(bird.fly()) # Eagle can fly!

super() se parent ka constructor call hota hai. Multiple inheritance mein Python MRO (Method Resolution Order) follow karta hai � left-to-right, depth-first.

Diamond problem: Jab do parent classes mein same method ho, Python MRO ke according decide karta hai kaunsa method call hoga. ClassName.__mro__ se dekh sakte ho.

Polymorphism: same method, different behavior

Polymorphism ka matlab hai � ek hi method name, lekin alag classes mein alag behavior. Function ko pata bhi nahi hota ki usse kaun sa object call kar raha hai.

polymorphism_example.py
class Animal:
 def __init__(self, name):
 self.name = name
 def speak(self):
 return "..."

class Dog(Animal):
 def speak(self):
 return "Bark!"

class Cat(Animal):
 def speak(self):
 return "Meow!"

class Duck(Animal):
 def speak(self):
 return "Quack!"

# Polymorphism in action
animals = [Dog("Tommy"), Cat("Kitty"), Duck("Donald")]
for animal in animals:
 print(f"{animal.name}: {animal.speak()}")

# Output:
# Tommy: Bark!
# Kitty: Meow!
# Donald: Quack!

Har animal ka speak() alag hai, lekin loop mein sab ek jaisa treat ho rahe hain. Yahi polymorphism ka power hai � ek interface se alag-alag objects kaam karte hain.

Encapsulation: data ko protect karna

Encapsulation mein tum variables ko private banate ho taaki bahar se direct access na ho. Python mein double underscore __ lagate hain.

encapsulation_example.py
class BankAccount:
 def __init__(self, balance):
 self.__balance = balance # private variable

 def deposit(self, amount):
 if amount > 0:
 self.__balance += amount
 return f"Deposited {amount}. Balance: {self.__balance}"
 return "Invalid amount"

 def withdraw(self, amount):
 if 0 < amount <= self.__balance:
 self.__balance -= amount
 return f"Withdrew {amount}. Balance: {self.__balance}"
 return "Insufficient funds"

 def get_balance(self):
 return self.__balance

account = BankAccount(1000)
print(account.deposit(500)) # Deposited 500. Balance: 1500
print(account.withdraw(200)) # Withdrew 200. Balance: 1300
print(account.get_balance()) # 1300
# account.__balance # AttributeError! Private hai

__balance ko directly access nahi kar sakte. Methods ke through hi interact karo � isse data safe rehta hai aur validation laga sakte ho.

Naming convention: __var (double underscore) se name mangling hoti hai. _var (single underscore) sirf convention hai � "yeh private hai, but technically accessible hai."

Concept grid: quick recap

INHERITANCE

Child class parent ki properties/methods leti hai. class Child(Parent) se inherit karo, super() se parent ko call karo.

POLYMORPHISM

Same method name, different classes mein alag behavior. Loop mein alag-alag objects ek jaisa kaam karte hain.

ENCAPSULATION

Data ko protect karna private variables se. __var se direct access roko, methods se interact karo.

Try it: Vehicle hierarchy banayeinCode edit karo aur Run dabao
Run Python dabayein

Quick check

Dog class banao jo Animal se inherit kare aur speak method override kare.

class Dog(Animal) se start karo, phir def speak(self) likho aur return "Bark!" return karo.

Common mistakes

Inheritance & Polymorphism complete?

Great. Ab File Handling seekho � files read/write karna, CSV parse karna, aur context managers use karna.