Python Inheritance Explained for Beginners – Parent, Child Classes, super() & Method Overriding

 

Python Inheritance – Parent, Child Classes, super() and Method Overriding Explained

📅 Published on Code with Py📂 Category: Python OOP⏱️ Reading time: ~15 min

You built your first class and created objects from it. Now picture this — you have a Vehicle class with 10 methods. You want to create a Car class and an ElectricCar class that have everything Vehicle has, plus a few extras. Do you copy and paste all 10 methods twice? That is messy and defeats the purpose of clean code. Inheritance is Python's solution — a child class automatically gets everything the parent has, and you only write the new parts. This guide teaches you how it works, why it matters, and how to use it confidently.

📋 Table of Contents

  1. What Is Inheritance and Why Does It Exist?
  2. Real-Life Analogy — Genes in a Family
  3. Your First Parent-Child Class
  4. Step-by-Step Trace — How Python Looks Up Attributes
  5. The super() Function — Calling the Parent from the Child
  6. Method Overriding — Changing Inherited Behaviour
  7. Multi-Level Inheritance
  8. Multiple Inheritance
  9. The isinstance() and issubclass() Functions
  10. Types of Inheritance — Quick Visual
  11. Real-Life Project Using Inheritance
  12. Practice Problems
  13. FAQ
  14. Summary

1. What Is Inheritance and Why Does It Exist?

Imagine you already have a working Employee class with name, salary, and a method that prints the employee's details. Now your company needs a Manager class. A Manager is still an Employee — same name, same salary — but with extra things like a team_size and a bonus calculation.

Without inheritance, you would write the Employee code again inside Manager. Change something in Employee later and you must remember to change Manager too. Bugs sneak in. Code doubles.

Inheritance lets the child class (Manager) automatically receive all the attributes and methods of the parent class (Employee). You write only what is new or different. One change in the parent instantly applies to all children.

Without Inheritance

Duplicate code in every class. Update one place → must update everywhere. Inconsistencies cause bugs. Harder to maintain as project grows.

With Inheritance

Child reuses parent code automatically. Update parent → all children benefit. Add only unique new behaviour in child. Clean, maintainable, professional.


2. Real-Life Analogy — Genes in a Family

🧬 A Child Inherits From Its Parents

When a child is born, they automatically inherit certain traits from their parents — eye colour, height, blood group. They did not have to "ask" for these — they just got them automatically. But the child also develops their own unique traits — their own personality, skills, and habits.

In Python, a child class automatically receives all the attributes and methods of the parent class — like biological traits. And just like a child can override a parent trait (different eye colour), a child class can override a parent method with its own version.


3. Your First Parent-Child Class

To create a child class that inherits from a parent, write the parent's name inside parentheses after the child class name.

# Parent class
class Animal:
    def __init__(self, name, sound):
        self.name  = name
        self.sound = sound

    def speak(self):
        print(f"{self.name} says {self.sound}!")

    def breathe(self):
        print(f"{self.name} is breathing...")

# Child class — inherits from Animal
class Dog(Animal):
    def fetch(self):
        print(f"{self.name} fetches the ball!")

# Create a Dog object
d1 = Dog("Bruno", "Woof")

d1.speak()     # Inherited from Animal
d1.breathe()   # Inherited from Animal
d1.fetch()     # Dog's own method
Bruno says Woof!
Bruno is breathing...
Bruno fetches the ball!
The Dog class has zero duplicate code from Animal. It gets __init__speak(), and breathe() for free just by writing class Dog(Animal). The only thing Dog adds is its unique fetch() method.

Check what a class inherited

print(Dog.__bases__)     # Which classes Dog inherits from
print(Dog.__mro__)       # Method Resolution Order
(<class '__main__.Animal'>,)
[<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>]
Every class in Python secretly inherits from a base class called object. That is why you see it at the end of every MRO list. This is where built-in methods like __str__ and __repr__ come from.

4. Step-by-Step Trace — How Python Looks Up Attributes

When you call d1.speak() on a Dog object, Python does not find speak immediately in Dog. Here is exactly how Python searches:

Trace of: d1.speak() — where d1 is a Dog object
1
Python checks: does d1 (the object itself) have a speak attribute? → No
2
Python checks: does class Dog have a speak method? → No
3
Python moves up the MRO: checks class Animal → Yes! speak() found here
4
Python calls Animal.speak() with self = d1
5
self.name = "Bruno", self.sound = "Woof" → prints "Bruno says Woof!"
Output: Bruno says Woof!

This search order — object → child class → parent class → grandparent → object — is called the Method Resolution Order (MRO).


5. The super() Function — Calling the Parent From the Child

When a child class needs to run the parent's __init__ along with its own extra setup, it uses super(). Think of it as the child saying — "run everything my parent does, then let me add my own stuff on top."

class Employee:
    def __init__(self, name, salary):
        self.name   = name
        self.salary = salary
        print(f"Employee created: {self.name}")

    def show(self):
        print(f"Name: {self.name} | Salary: ₹{self.salary}")


class Manager(Employee):
    def __init__(self, name, salary, team_size):
        super().__init__(name, salary)   # Run Employee's __init__ first
        self.team_size = team_size       # Then add Manager's extra data
        print(f"Manager created with team of {team_size}")

    def show(self):
        super().show()                   # Show parent's info first
        print(f"Team Size: {self.team_size}")

m1 = Manager("Rohan", 80000, 12)
print()
m1.show()
Employee created: Rohan
Manager created with team of 12

Name: Rohan | Salary: ₹80000
Team Size: 12
Trace of: Manager("Rohan", 80000, 12)
1
Python creates a new Manager object in memory
2
Calls Manager.__init__(self, "Rohan", 80000, 12)
3
super().__init__("Rohan", 80000) → runs Employee.__init__
4
self.name = "Rohan", self.salary = 80000 → prints "Employee created: Rohan"
5
Back in Manager.__init__ → self.team_size = 12
6
Prints "Manager created with team of 12"
m1 now has name, salary (from Employee) + team_size (from Manager)
Always call super().__init__() inside the child's __init__ — otherwise the parent's attributes will never be set on the object and you will get an AttributeError when accessing them later.

6. Method Overriding — Changing Inherited Behaviour

Sometimes the parent's method does not fit the child perfectly. Method overriding lets the child rewrite a method it inherited — keeping the same name but giving it new behaviour. Python always uses the child's version when it exists.

class Vehicle:
    def start(self):
        print("Vehicle is starting with a key...")

    def fuel_type(self):
        print("This vehicle uses petrol.")


class ElectricCar(Vehicle):
    # Override start() — electric cars start differently
    def start(self):
        print("Electric car is starting silently with a button...")

    # Override fuel_type() — completely different
    def fuel_type(self):
        print("This vehicle uses electricity, not petrol.")


# Regular car — uses parent methods
v1 = Vehicle()
v1.start()
v1.fuel_type()

print()

# Electric car — uses overridden methods
ec = ElectricCar()
ec.start()
ec.fuel_type()
Vehicle is starting with a key...
This vehicle uses petrol.

Electric car is starting silently with a button...
This vehicle uses electricity, not petrol.

Override and also call the parent's version

class Animal:
    def describe(self):
        print("I am a living creature.")

class Dog(Animal):
    def describe(self):
        super().describe()           # Also run parent version
        print("I am specifically a dog.")   # Then add own detail

d = Dog()
d.describe()
I am a living creature.
I am specifically a dog.
Calling super().method() inside an overridden method lets you keep the parent's behaviour AND add to it — like the child getting their parent's traits plus developing new ones.

7. Multi-Level Inheritance

A child can itself be a parent to another class. This creates a chain — grandparent → parent → child. Each level inherits everything from all levels above it.

Animal
↓ inherits
Dog (child of Animal)
↓ inherits
GoldenRetriever (child of Dog)
class Animal:
    def breathe(self):
        print("Breathing...")

class Dog(Animal):     # Dog inherits Animal
    def bark(self):
        print("Woof!")

class GoldenRetriever(Dog):   # GoldenRetriever inherits Dog
    def fetch(self):
        print("Fetching the stick!")

gr = GoldenRetriever()

# Has all methods from all 3 levels
gr.breathe()   # From Animal (grandparent)
gr.bark()      # From Dog (parent)
gr.fetch()     # From GoldenRetriever (own)
print(GoldenRetriever.__mro__)
Breathing...
Woof!
Fetching the stick!
[<class 'GoldenRetriever'>, <class 'Dog'>, <class 'Animal'>, <class 'object'>]
The MRO shows exactly the order Python searches for methods — from the most specific class (GoldenRetriever) up to the most general (object). This search path guarantees Python always finds the right version of a method.

8. Multiple Inheritance

Python allows a class to inherit from more than one parent at the same time. The child gets everything from all parents.

class Swimmer:
    def swim(self):
        print("Swimming in the water...")

class Runner:
    def run(self):
        print("Running on land...")

class Triathlete(Swimmer, Runner):   # Inherits BOTH
    def compete(self):
        self.swim()
        self.run()
        print("Triathlete finishes the race!")

athlete = Triathlete()
athlete.compete()
Swimming in the water...
Running on land...
Triathlete finishes the race!
Multiple inheritance is powerful but can cause confusion when two parent classes have a method with the same name. Python resolves this using MRO — it picks the method from whichever parent is listed first. Use multiple inheritance carefully and keep it simple.

9. The isinstance() and issubclass() Functions

These two built-in functions let you check inheritance relationships at runtime — very useful for writing defensive code.

class Animal: pass
class Dog(Animal): pass
class Cat(Animal): pass

d = Dog()
c = Cat()

# isinstance — is this object an instance of this class?
print(isinstance(d, Dog))     # True  — d IS a Dog
print(isinstance(d, Animal))  # True  — Dog inherits Animal
print(isinstance(d, Cat))     # False — d is not a Cat

print()

# issubclass — is this class a subclass of another?
print(issubclass(Dog, Animal))   # True
print(issubclass(Cat, Animal))   # True
print(issubclass(Dog, Cat))      # False
True
True
False

True
True
False
Notice that isinstance(d, Animal) returns True even though d was created from Dog. That is inheritance at work — a Dog object IS an Animal because Dog inherits from Animal.

10. Types of Inheritance — Quick Visual

TypeStructureExample
SingleOne parent → one childAnimal → Dog
Multi-LevelChain: A → B → CAnimal → Dog → GoldenRetriever
MultipleTwo parents → one childSwimmer + Runner → Triathlete
HierarchicalOne parent → many childrenAnimal → Dog, Cat, Bird
HybridMix of the above typesCombination of multi-level + multiple

11. Real-Life Project — School Management System

class Person:
    """Base class for any person in the school."""
    def __init__(self, name, age):
        self.name = name
        self.age  = age

    def introduce(self):
        print(f"Hi, I am {self.name}, age {self.age}.")


class Student(Person):
    def __init__(self, name, age, marks):
        super().__init__(name, age)
        self.marks = marks

    def grade(self):
        if   self.marks >= 90: return "A"
        elif self.marks >= 75: return "B"
        elif self.marks >= 50: return "C"
        else:                   return "Fail"

    def report(self):
        self.introduce()
        print(f"Marks: {self.marks} | Grade: {self.grade()}")


class Teacher(Person):
    def __init__(self, name, age, subject, exp_years):
        super().__init__(name, age)
        self.subject   = subject
        self.exp_years = exp_years

    def report(self):
        self.introduce()
        print(f"Subject: {self.subject} | Experience: {self.exp_years} years")


class HeadTeacher(Teacher):
    def __init__(self, name, age, subject, exp_years, school):
        super().__init__(name, age, subject, exp_years)
        self.school = school

    def report(self):
        super().report()
        print(f"School: {self.school} | Role: Head Teacher")

# Create objects from all levels
s1  = Student("Rohan",  17, 88)
t1  = Teacher("Mrs Priya", 35, "Python", 10)
ht1 = HeadTeacher("Mr Arjun", 50, "Maths", 25, "Code with Py Academy")

print("=== STUDENT ===")
s1.report()
print("\n=== TEACHER ===")
t1.report()
print("\n=== HEAD TEACHER ===")
ht1.report()
=== STUDENT ===
Hi, I am Rohan, age 17.
Marks: 88 | Grade: B

=== TEACHER ===
Hi, I am Mrs Priya, age 35.
Subject: Python | Experience: 10 years

=== HEAD TEACHER ===
Hi, I am Mr Arjun, age 50.
Subject: Maths | Experience: 25 years
School: Code with Py Academy | Role: Head Teacher

12. Practice Problems

Beginner level

  1. Create a Vehicle parent with fuel() method. Create Car and Bike children that override fuel()
  2. Create a Shape parent. Add Circle and Square children with area() method each
  3. Make a Person parent. Create Student and Teacher children with extra attributes
  4. Test isinstance() on your created objects
  5. Use super() in each child's __init__

Intermediate level

  1. Create Animal → Pet → Dog (multi-level). Dog must use methods from all 3 levels
  2. Create Flyable and Swimmable classes. Make Duck inherit both
  3. Override __str__ in a child class to print custom info
  4. Build Employee → Manager → Director chain with salary and bonus
  5. Use issubclass() to verify your hierarchy

Real-life projects

  1. Build a Bank Account hierarchy: Account → SavingsAccount → FixedDeposit
  2. Build a School: Person → Student and Teacher → HeadTeacher
  3. Create a Phone hierarchy: Device → Phone → Smartphone → iPhone
  4. Model a Game: Character → Warrior, Mage, Archer with unique attacks
  5. Build an E-commerce product hierarchy with discount methods

Think deeper

  1. What happens if parent and child both have __init__ but child doesn't call super()?
  2. Can a child class access parent's private attributes?
  3. What order do methods resolve in multiple inheritance?
  4. Can you override __init__ without using super()?
  5. What does every Python class secretly inherit from?

13. FAQ

Q1. What is the difference between inheritance and creating a new class from scratch?

When you create a class from scratch, you write every attribute and method yourself. With inheritance, the child class automatically receives everything the parent already has — you only write what is new or different. Inheritance saves code, prevents duplication, and ensures consistency across related classes.

Q2. What exactly does super() do?

super() returns a temporary reference to the parent class. You use it to call the parent's methods from inside the child — most commonly to run the parent's __init__ before adding the child's own setup. It uses the MRO to find the correct parent in cases of multiple inheritance.

Q3. Can a child class override any method from the parent?

Yes — any method can be overridden, including __init____str____repr__, and your own custom methods. When you define a method in the child with the same name as one in the parent, Python always uses the child's version when calling it on a child object.

Q4. What happens if I don't call super().__init__() in the child?

The parent's __init__ never runs. The parent's attributes — like self.name and self.salary — are never created on the object. When any method tries to access them later, Python raises an AttributeError. Always call super().__init__() unless you intentionally want to skip the parent's setup.

Q5. What is the difference between isinstance() and type()?

type(obj) returns the exact class the object was created from — it does not consider inheritance. isinstance(obj, Class) returns True if the object belongs to that class OR any of its parent classes. For example, if Dog inherits Animal, isinstance(dog, Animal) is True but type(dog) == Animal is False.

Q6. Should I always use inheritance or are there times to avoid it?

Use inheritance when there is a clear "is-a" relationship — a Dog IS an Animal, a Manager IS an Employee. Avoid it when the relationship is "has-a" instead — a Car HAS an Engine (use composition, not inheritance). Overusing inheritance creates tightly coupled code that is hard to change. Keep inheritance chains shallow — 2 to 3 levels is usually enough.


✅ Quick Summary — What You Learned

  • Inheritance lets a child class automatically receive all attributes and methods of its parent
  • Syntax: class Child(Parent): — the parent name goes in parentheses
  • Python searches for methods using the MRO — child → parent → grandparent → object
  • super() gives a reference to the parent — used to call parent's __init__ and methods
  • Method overriding — child defines same method name as parent → Python uses child's version
  • Multi-level inheritance creates a chain: A → B → C — C gets everything from A and B
  • Multiple inheritance — one child inherits from two or more parents simultaneously
  • isinstance() checks if an object belongs to a class or its parents
  • issubclass() checks if one class is derived from another
  • Every Python class secretly inherits from the built-in object class

"Inheritance is not copying — it is evolving. A child class does not repeat the past, it builds on it."

— Code with Py

Found this post helpful? Share it with someone learning Python OOP. Build the projects, test the traces — and drop your questions in the comments!

Comments

Popular posts from this blog

Python Strings – A Complete Guide for Beginners (with Examples)

Python Control Flow Statements – Complete Guide with Real-Life Examples

Python Loops Explained – for Loop and while Loop with Examples and Output