You inherit your eye colour from your parents, yet you also have your own unique talents. In programming, inheritance lets a new class (child) reuse the attributes and methods of an existing class (parent), and add or change its own. It is the biggest code-reuse tool in OOP. In this blog you will master all five types of inheritance, super(), method overriding, the Method Resolution Order (MRO) and the famous diamond problem.
Watch the video first, then use this blog as your notes. Every section has runnable examples, and at the end you will find a "Predict the Output" quiz, practice challenges, a self-check list, interview questions and a quick summary.
How to use this blog: Open the Python shell (type python in your terminal) or any online Python editor and type every example yourself. Change the values, break the code and see what happens — that is how concepts really stick.
Inheritance is a mechanism where a child class (subclass / derived class) acquires the properties and behaviour of a parent class (superclass / base class). It models an "is-a" relationship: a Dog is an Animal, a Car is a Vehicle.
class Parent:
# parent's attributes and methods
pass
class Child(Parent): # Child inherits from Parent
# extra attributes and methods
passclass Animal:
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name} is eating")
def sleep(self):
print(f"{self.name} is sleeping")
class Dog(Animal):
def bark(self): # new method only for Dog
print(f"{self.name} says Woof!")
d = Dog("Tommy")
d.eat() # Tommy is eating <- inherited
d.sleep() # Tommy is sleeping <- inherited
d.bark() # Tommy says Woof! <- its ownDog did not define __init__, eat or sleep — it got them all from Animal.
When the child defines its own __init__, the parent's __init__ is not called automatically. Use super() to call it.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
return f"{self.name}, {self.age} years"
class Student(Person):
def __init__(self, name, age, roll):
super().__init__(name, age) # reuse the parent's constructor
self.roll = roll # add a new attribute
def info(self): # override and extend
return super().info() + f", Roll No: {self.roll}"
s = Student("Riya", 17, 21)
print(s.info()) # Riya, 17 years, Roll No: 21Common bug: If the child defines __init__ and forgets super().__init__(...), the parent's attributes are never created, and you get AttributeError: 'Student' object has no attribute 'name'.
A child can replace a parent method by defining a method with the same name.
class Vehicle:
def wheels(self):
return 4
class Bike(Vehicle):
def wheels(self): # overrides
return 2
class Car(Vehicle):
pass # uses parent version
print(Bike().wheels(), Car().wheels()) # 2 4Single Multilevel Hierarchical Multiple Hybrid
A A A A B A
| | / | \ \ / / \
B B B C D C B C
| \ /
C Dclass Father:
def skills(self):
return "Gardening"
class Son(Father):
pass
print(Son().skills()) # Gardeningclass Grandfather:
def house(self):
return "Ancestral house"
class Father(Grandfather):
def car(self):
return "Car"
class Son(Father):
def bike(self):
return "Bike"
s = Son()
print(s.house(), "|", s.car(), "|", s.bike())
# Ancestral house | Car | Bikeclass Shape:
def __init__(self, name):
self.name = name
class Circle(Shape):
def __init__(self, r):
super().__init__("Circle")
self.r = r
def area(self):
return round(3.14159 * self.r ** 2, 2)
class Square(Shape):
def __init__(self, side):
super().__init__("Square")
self.side = side
def area(self):
return self.side ** 2
for s in [Circle(2), Square(3)]:
print(s.name, s.area()) # Circle 12.57, Square 9Python supports it (Java does not, for classes!).
class Camera:
def click(self):
return "Photo clicked"
class Phone:
def call(self):
return "Calling..."
class Smartphone(Camera, Phone):
pass
sp = Smartphone()
print(sp.click(), "|", sp.call()) # Photo clicked | Calling...class A:
def show(self):
print("A")
class B(A):
def show(self):
print("B")
class C(A):
def show(self):
print("C")
class D(B, C): # diamond: D -> B, C -> A
pass
D().show() # B - which one and why? See MRO below!| Type | Structure | Example |
|---|---|---|
| Single | A → B | Person → Student |
| Multilevel | A → B → C | Grandfather → Father → Son |
| Hierarchical | A → B, A → C | Shape → Circle, Square |
| Multiple | A, B → C | Camera, Phone → Smartphone |
| Hybrid | Mix of the above | Diamond: A → B, C → D |
When several parents have the same method, which one runs? Python decides using the MRO, calculated by the C3 linearization algorithm. Simple rule: child first, then parents from left to right, and a common ancestor only after all its children.
print([cls.__name__ for cls in D.mro()])
# ['D', 'B', 'C', 'A', 'object']So D().show() finds show in B first. The order of parents in class D(B, C) matters!
class A:
def __init__(self):
print("A init")
class B(A):
def __init__(self):
print("B init")
super().__init__()
class C(A):
def __init__(self):
print("C init")
super().__init__()
class D(B, C):
def __init__(self):
print("D init")
super().__init__()
D()
# D init
# B init
# C init <- B's super() goes to C (next in MRO), not directly to A
# A init <- A runs only ONCE: the diamond problem is solvedclass Animal: pass
class Dog(Animal): pass
d = Dog()
print(isinstance(d, Dog)) # True
print(isinstance(d, Animal)) # True - a Dog IS an Animal
print(issubclass(Dog, Animal)) # True
print(issubclass(Animal, Dog)) # False
print(Dog.__bases__) # (<class '__main__.Animal'>,)
print(issubclass(bool, int)) # True - even built-ins use inheritance!Did you know?: Every class in Python 3 automatically inherits from object. That is where default methods like __str__, __eq__ and __init__ come from.
class Parent:
def __init__(self):
self.public = "public"
self._protected = "protected"
self.__private = "private" # becomes _Parent__private
class Child(Parent):
def show(self):
print(self.public) # OK
print(self._protected) # OK
# print(self.__private) # AttributeError (looks for _Child__private)
Child().show()Inheritance is for "is-a" relationships. For "has-a" relationships, use composition (an object containing another object).
class Engine:
def start(self):
return "Engine started"
class Car: # a Car HAS an Engine (not IS an Engine)
def __init__(self):
self.engine = Engine()
def drive(self):
return self.engine.start() + " - Car is moving"
print(Car().drive()) # Engine started - Car is movingDesign tip: Prefer composition when the relationship is not truly "is-a". Deep inheritance chains (5+ levels) become hard to understand and maintain.
class Employee:
def __init__(self, name, base_salary):
self.name = name
self.base_salary = base_salary
def salary(self):
return self.base_salary
def __str__(self):
return f"{type(self).__name__:<10} {self.name:<8} Rs {self.salary():>8,}"
class Manager(Employee):
def __init__(self, name, base_salary, team_size):
super().__init__(name, base_salary)
self.team_size = team_size
def salary(self): # override: bonus per team member
return super().salary() + self.team_size * 2000
class Intern(Employee):
def salary(self): # override: half salary
return self.base_salary // 2
staff = [Employee("Aman", 40000), Manager("Riya", 60000, 5), Intern("Karan", 30000)]
for person in staff:
print(person)Employee Aman Rs 40,000
Manager Riya Rs 70,000
Intern Karan Rs 15,000Don't run the code yet! Write your answers in a notebook, then check the Answers section at the end of this blog.
Q1. What is the output?
class A:
def hi(self):
return "A"
class B(A):
pass
print(B().hi())Q2. What happens?
class X:
def __init__(self):
self.v = 1
class Y(X):
def __init__(self):
self.w = 2
print(Y().v)Q3. What is the output?
class F:
def m(self):
return "F"
class M:
def m(self):
return "M"
class K(F, M):
pass
class L(M, F):
pass
print(K().m(), L().m())Q4. What is the output?
class P:
def greet(self):
return "Hello"
class C(P):
def greet(self):
return super().greet() + " World"
print(C().greet())Q5. What is the output?
class A: pass
class B(A): pass
print(isinstance(B(), A), issubclass(A, B))Q6. What is the MRO of D?
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): passQ7. Which type of inheritance is this?
class A: pass
class B(A): pass
class C(B): passQ8. Does Java support multiple inheritance of classes? Does Python?
Solve these from easy to tricky:
Solutions for challenges 2 and 4 are in the Answers section.
Tick off each skill once you are confident:
Q: What is inheritance?
An OOP mechanism by which a child class acquires the attributes and methods of a parent class, enabling code reuse.
Q: What are the types of inheritance in Python?
Single, multiple, multilevel, hierarchical and hybrid.
Q: What is the use of super()?
It returns a proxy object that delegates method calls to the next class in the MRO — most commonly used to call the parent's __init__ or an overridden method.
Q: What is MRO?
Method Resolution Order — the order in which Python searches classes for a method, computed using C3 linearization. View it with ClassName.mro().
Q: What is the diamond problem and how does Python solve it?
When a class inherits from two classes that share a common parent, it's ambiguous which path to follow. Python's MRO gives a single consistent order and ensures the common ancestor is visited only once.
Q: Are private members inherited?
They exist in the child object, but under the mangled name _Parent__x, so the child cannot access them as self.__x.
| Concept | Syntax / Key point |
|---|---|
| Inherit | class B(A): |
| Multiple | class C(A, B): |
| Call parent init | super().__init__(x) |
| Call parent method | super().method() |
| See MRO | C.mro() |
| Check object | isinstance(obj, A) |
| Check class | issubclass(B, A) |
# Challenge 2
class Person:
def __init__(self, name):
self.name = name
class Employee(Person):
def __init__(self, name, emp_id):
super().__init__(name)
self.emp_id = emp_id
class Manager(Employee):
def __init__(self, name, emp_id, team):
super().__init__(name, emp_id)
self.team = team
m = Manager("Riya", "E101", ["Aman", "Karan"])
print(m.name, m.emp_id, m.team) # Riya E101 ['Aman', 'Karan']
# Challenge 4
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
self.balance += amount
class SavingsAccount(BankAccount):
def __init__(self, owner, balance=0, rate=0.04):
super().__init__(owner, balance)
self.rate = rate
def add_interest(self):
self.balance += self.balance * self.rate
acc = SavingsAccount("Aman", 10000)
acc.add_interest()
print(acc.balance) # 10400.0Congratulations! You have completed the core OOP concepts of this Python series: classes and objects, encapsulation, polymorphism and inheritance. Keep practising by building small projects — a library system, a school management app or a simple game — using all four pillars together.
If this blog helped you, watch the full video above, subscribe to the channel and share it with a friend who is learning Python. Happy coding!