Python Inheritance Explained for Beginners – Parent, Child Classes, super() & Method Overriding
Python Inheritance – Parent, Child Classes, super() and Method Overriding Explained
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
- What Is Inheritance and Why Does It Exist?
- Real-Life Analogy — Genes in a Family
- Your First Parent-Child Class
- Step-by-Step Trace — How Python Looks Up Attributes
- The super() Function — Calling the Parent from the Child
- Method Overriding — Changing Inherited Behaviour
- Multi-Level Inheritance
- Multiple Inheritance
- The isinstance() and issubclass() Functions
- Types of Inheritance — Quick Visual
- Real-Life Project Using Inheritance
- Practice Problems
- FAQ
- 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!
__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'>]
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:
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
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.
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.
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'>]
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!
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
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
| Type | Structure | Example |
|---|---|---|
| Single | One parent → one child | Animal → Dog |
| Multi-Level | Chain: A → B → C | Animal → Dog → GoldenRetriever |
| Multiple | Two parents → one child | Swimmer + Runner → Triathlete |
| Hierarchical | One parent → many children | Animal → Dog, Cat, Bird |
| Hybrid | Mix of the above types | Combination 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
- Create a Vehicle parent with fuel() method. Create Car and Bike children that override fuel()
- Create a Shape parent. Add Circle and Square children with area() method each
- Make a Person parent. Create Student and Teacher children with extra attributes
- Test isinstance() on your created objects
- Use super() in each child's __init__
Intermediate level
- Create Animal → Pet → Dog (multi-level). Dog must use methods from all 3 levels
- Create Flyable and Swimmable classes. Make Duck inherit both
- Override __str__ in a child class to print custom info
- Build Employee → Manager → Director chain with salary and bonus
- Use issubclass() to verify your hierarchy
Real-life projects
- Build a Bank Account hierarchy: Account → SavingsAccount → FixedDeposit
- Build a School: Person → Student and Teacher → HeadTeacher
- Create a Phone hierarchy: Device → Phone → Smartphone → iPhone
- Model a Game: Character → Warrior, Mage, Archer with unique attacks
- Build an E-commerce product hierarchy with discount methods
Think deeper
- What happens if parent and child both have __init__ but child doesn't call super()?
- Can a child class access parent's private attributes?
- What order do methods resolve in multiple inheritance?
- Can you override __init__ without using super()?
- 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 parentsissubclass()checks if one class is derived from another- Every Python class secretly inherits from the built-in
objectclass
"Inheritance is not copying — it is evolving. A child class does not repeat the past, it builds on it."
— Code with PyFound this post helpful? Share it with someone learning Python OOP. Build the projects, test the traces — and drop your questions in the comments!
Comments
Post a Comment