Home Blogs All Courses
python c OA ( Online Assessment ) Amazon Web Services (AWS) leetcode dsa algorithms java Machine learning
Get Referral
Login Sign Up

Inheritance in Python: Types, super(), Method Overriding & MRO Explained


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.

1. What is Inheritance?

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
    pass

Why use inheritance?

  • Code reusability — write common code once in the parent.
  • Extensibility — add new features in child classes without touching the parent.
  • Run-time polymorphism — children can override parent methods.
  • Real-world modelling — natural hierarchies (Person → Student → GraduateStudent).

2. Your First Inheritance Example

class 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 own

Dog did not define __init__, eat or sleep — it got them all from Animal.

3. The super() Function

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: 21

Common 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'.

4. Method Overriding

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 4

5. Types of Inheritance

Single        Multilevel      Hierarchical        Multiple          Hybrid
   A               A                A              A     B             A
   |               |              / | \             \   /             / \
   B               B             B  C  D              C              B   C
                   |                                                  \ /
                   C                                                   D

5.1 Single inheritance — one parent, one child

class Father:
    def skills(self):
        return "Gardening"

class Son(Father):
    pass

print(Son().skills())          # Gardening

5.2 Multilevel inheritance — a chain

class 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 | Bike

5.3 Hierarchical inheritance — one parent, many children

class 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 9

5.4 Multiple inheritance — many parents, one child

Python 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...

5.5 Hybrid inheritance — a combination (e.g. the diamond)

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!
TypeStructureExample
SingleA → BPerson → Student
MultilevelA → B → CGrandfather → Father → Son
HierarchicalA → B, A → CShape → Circle, Square
MultipleA, B → CCamera, Phone → Smartphone
HybridMix of the aboveDiamond: A → B, C → D

6. Method Resolution Order (MRO) and the Diamond Problem

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!

super() follows the MRO, not just the parent

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 solved

7. Useful Built-in Functions

class 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.

8. Inheritance and Private Members

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()

9. Inheritance vs Composition

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 moving

Design tip: Prefer composition when the relationship is not truly "is-a". Deep inheritance chains (5+ levels) become hard to understand and maintain.

10. Mini Project: Employee Management System

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,000

Quiz: Predict the Output

Don'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): pass

Q7. Which type of inheritance is this?

class A: pass
class B(A): pass
class C(B): pass

Q8. Does Java support multiple inheritance of classes? Does Python?

Practice Challenges

Solve these from easy to tricky:

  1. Create a Vehicle class with brand and a info() method; inherit Car and Bike and override info().
  2. Create a multilevel chain Person → Employee → Manager using super() in every __init__.
  3. Create a FlyingCar class using multiple inheritance from Car and Plane. Print its MRO.
  4. Build a BankAccount parent and a SavingsAccount child that adds interest.
  5. Rewrite a "Car is-an Engine" design using composition instead of inheritance.

Solutions for challenges 2 and 4 are in the Answers section.

Self-Check: Can You Do This?

Tick off each skill once you are confident:

Frequently Asked Interview Questions

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.

Summary

  • Inheritance lets a child class reuse a parent's attributes and methods: class Child(Parent):.
  • Use super().__init__(...) when the child has its own constructor.
  • Method overriding: the child redefines a parent method; extend it with super().
  • Five types: single, multilevel, hierarchical, multiple, hybrid.
  • Python resolves methods using the MRO (C3 linearization), which also solves the diamond problem.
  • All classes inherit from object. Prefer composition for "has-a" relationships.

Quick Revision Cheat Sheet

ConceptSyntax / Key point
Inheritclass B(A):
Multipleclass C(A, B):
Call parent initsuper().__init__(x)
Call parent methodsuper().method()
See MROC.mro()
Check objectisinstance(obj, A)
Check classissubclass(B, A)

Answers

Quiz answers

  1. A – inherited from A.
  2. AttributeError – Y's __init__ never called super().__init__().
  3. F M – left-most parent first in the MRO.
  4. Hello World
  5. True False
  6. D → B → C → A → object
  7. Multilevel inheritance.
  8. Java: No (only via interfaces). Python: Yes.

Challenge solutions

# 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.0

Conclusion

Congratulations! 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!