Inheritance in Python – A Beginner-Friendly but Deep Dive

Whether you are beginning your journey in OOPS or preparing for your technical interview, understanding Inheritance in Python is essential.

In this guide, we will walk you through every concept of Inheritance in Python.

Python, being an object-oriented programming language, provides the power of inheritance – a concept that allows a class (child) to acquire the properties and behaviors (methods and attributes) of another class (parent).

Whether you’re a beginner just starting with classes, or a seasoned developer brushing up for an interview or refactoring code, this article on Inheritance in Python will walk you through everything you need to know about inheritance in Python.

Table of Contents

🚀 What is Inheritance? (Inheritance in Python)

Inheritance is one of the four pillars of OOP (Object-Oriented Programming), alongside Encapsulation, Polymorphism, and Abstraction.

Definition: Inheritance allows a class to derive or inherit the properties and behaviors from another class.

✅ Benefits of Inheritance:

  • Code reuse
  • Logical class hierarchy
  • Easier maintenance
  • Polymorphic behavior

📦 Basic Terminology

  • Base class (Parent class): The class whose properties and methods are inherited.
  • Derived class (Child class): The class that inherits from the base class.

🛠️ Basic Syntax of Inheritance in Python

class Parent:

    def greet(self):

        print("Hello from Parent")

class Child(Parent):

    pass

obj = Child()

obj.greet()  # Output: Hello from Parent

🔍 Here, Child class inherits the greet() method from Parent without redefining it.


🧠 Understanding Common Python OOP Terms (Beginner-Friendly)

When you’re learning object-oriented programming in Python, you’ll often come across special words like:

  • self
  • __init__
  • super()
  • __str__
  • __mro__

Let’s break these down in simple language.


1. 🔁 self – The Object Itself

Think of self as “me” or “this object”.

✅ What it does:

  • Refers to the current object of the class.
  • Allows each object to keep its own data.

📦 Example:

class Dog:

    def __init__(self, name):

        self.name = name  # self.name is this dog’s name

    def bark(self):

        print(f"{self.name} says woof!")

d1 = Dog("Buddy")

d2 = Dog("Max")

d1.bark()  # Buddy says woof!

d2.bark()  # Max says woof!

Without self, you wouldn’t be able to store different names for each dog. It helps tie the data to the specific object.


2. 🚪 __init__ – The Constructor (Object Builder)

“Init” stands for initialize. It sets up the object when it’s created.

✅ What it does:

  • Runs automatically when you create a new object.
  • Initializes object properties (like setting name, age, etc.)

📦 Example:

class User:

    def __init__(self, username, email):

        self.username = username

        self.email = email

u = User("john_doe", "john@example.com")

print(u.username)  # john_doe

It’s like a blueprint for how every new object should be built.


3. 📞 super() – Talk to the Parent Class

Think of super() as calling your parents for help.

✅ What it does:

  • Lets the child class access methods or constructors of the parent class.
  • Commonly used inside __init__() to call the parent’s setup code.

📦 Example:

class Animal:

    def __init__(self):

        print("Animal created")

class Dog(Animal):

    def __init__(self):

        super().__init__()  # Calls Animal's __init__()

        print("Dog created")

d = Dog()

🟢 Output:

Animal created

Dog created


4. 🪞 __str__ – The String Version of the Object

Controls what shows when you print the object.

✅ What it does:

  • Used when you print an object.
  • Makes output more readable.

📦 Example:

class Book:

    def __init__(self, title):

        self.title = title

    def __str__(self):

        return f"Book: {self.title}"

b = Book("Python 101")

print(b)  # Book: Python 101

Without __str__, you’d just see something like <__main__.Book object at 0x123456>.


5. 🧮 __mro__ – Method Resolution Order

Used in multiple inheritance to decide which method gets called first.

✅ What it does:

  • Shows the order in which Python looks for methods.
  • Useful when multiple parent classes define the same method.

📦 Example:

class A: pass

class B(A): pass

class C(B): pass

print(C.__mro__)

🟢 Output:

(<class ‘__main__.C’>, <class ‘__main__.B’>, <class ‘__main__.A’>, <class ‘object’>)

This tells Python: “When calling a method from C, look in C, then B, then A, then object.”


🎁 Bonus: Dunder Methods

Words like __init__, __str__, etc., are called “dunder methods” (short for double underscore).

Python uses these special methods to give objects certain behavior automatically.

Common Dunder Methods:

MethodPurpose
__init__Object constructor
__str__String representation for print()
__len__Called by len()
__eq__Called by == comparison
__add__Called by + operator

🧠 In Simple Words…

TermSimple Meaning
self“This object”
__init__Setup when object is created
super()Call the parent class’s method
__str__What shows when you print the object
__mro__The order Python looks for methods in classes

🧩 How They All Work Together

Let’s combine everything in one short example:

class Person:

    def __init__(self, name):

        self.name = name

    def greet(self):

        print(f"Hi, I'm {self.name}")

class Student(Person):

    def __init__(self, name, grade):

        super().__init__(name)  # call parent constructor

        self.grade = grade

    def greet(self):

        super().greet()  # also use parent method

        print(f"I'm in grade {self.grade}")

s = Student("Alice", 10)

s.greet()

🟢 Output:

Hi, I’m Alice

I’m in grade 10

When you’re starting out with object-oriented programming in Python, these terms might seem like a secret language. But once you understand what they do, you’ll see they make your code more organized, reusable, and powerful.

The best way to get comfortable with them is to build small projects and play with inheritance, constructors, and method overrides.


🧬 The super() Function

To call the parent class’s constructor or methods from the child class, use super():

class Parent:

    def __init__(self):

        print("Parent constructor")

class Child(Parent):

    def __init__(self):

        super().__init__()  # Calls Parent's constructor

        print("Child constructor")

obj = Child()

🔄 Output:

Parent constructor

Child constructor


🧱 Types of Inheritance in Python

Python supports five types of inheritance:

1. Single Inheritance

One child class inherits from one parent class.

class Animal:

    def sound(self):

        print("Some sound")

class Dog(Animal):

    def bark(self):

        print("Bark")

dog = Dog()

dog.sound()  # Inherited

dog.bark()   # Own method


2. Multiple Inheritance

A child class inherits from more than one parent class.

class Father:

    def skills(self):

        print("Gardening, Programming")

class Mother:

    def skills(self):

        print("Cooking, Art")

class Child(Father, Mother):

    pass

c = Child()

c.skills()  # Which one gets called?

🔍 Output depends on Method Resolution Order (MRO). In this case, Father comes first, so his method is called.


3. Multilevel Inheritance

A child inherits from a parent, and that parent itself inherits from another class.

class Grandparent:

    def home(self):

        print("Big house")

class Parent(Grandparent):

    def car(self):

        print("Sedan")

class Child(Parent):

    def bike(self):

        print("Mountain bike")

c = Child()

c.home()  # From Grandparent

c.car()   # From Parent

c.bike()  # Own method


4. Hierarchical Inheritance

Multiple child classes inherit from a single parent class.

class Parent:

    def values(self):

        print("Discipline, Honesty")

class Child1(Parent):

    pass

class Child2(Parent):

    pass

c1 = Child1()

c2 = Child2()

c1.values()

c2.values()


5. Hybrid Inheritance

A combination of two or more types of inheritance.

class A:

    def method(self):

        print("A method")

class B(A):

    pass

class C:

    def method(self):

        print("C method")

class D(B, C):

    pass

d = D()

d.method()

Here, D inherits from both B and C, and since B inherits from A, it’s a hybrid model. Python resolves method calls using MRO (Method Resolution Order).


🧠 Understanding MRO (Method Resolution Order)

When dealing with multiple inheritance, Python uses C3 Linearization (MRO) to determine the order in which base classes are searched.

You can check MRO using:

print(D.__mro__)

# or

help(D)


🪄 Method Overriding in Inheritance

If the child class has a method with the same name as one in the parent class, it overrides the parent’s method.

class Parent:

    def show(self):

        print("Parent show")

class Child(Parent):

    def show(self):

        print("Child show")

obj = Child()

obj.show()  # Output: Child show

You can still call the parent method using super():

class Child(Parent):

    def show(self):

        super().show()

        print("Child show")


⚙️ Constructor Inheritance

If you don’t define an __init__() method in the child class, the parent’s constructor is automatically called.

But if you do define one, you need to call the parent constructor explicitly using super().


🔒 Private Members and Inheritance

Python doesn’t have true private variables, but by convention, names with double underscores (__var) are considered private.

class Parent:

    def __init__(self):

        self.__private = "secret"

class Child(Parent):

    def show(self):

        print(self.__private)  # Error

c = Child()

c.show()

🔴 This will raise an AttributeError because __private is name-mangled.


📁 Real-World Use Case: DRY Principle

Inheritance is the DRY principle (Don’t Repeat Yourself) in action.

Example:

class User:

    def __init__(self, username):

        self.username = username

    def login(self):

        print(f"{self.username} logged in")

class Admin(User):

    def delete_user(self, user):

        print(f"{self.username} deleted {user.username}")

admin = Admin("Alice")

user = User("Bob")

admin.login()

admin.delete_user(user)


❓Common Pitfalls to Avoid

  • Overusing inheritance: Prefer composition when appropriate.
  • Not calling super() in child constructors.
  • Assuming MRO will always behave as expected: Always check __mro__.

📚 Summary Table

ConceptDescription
class Child(Parent)Basic inheritance syntax
super()Call parent methods/constructors
Method OverridingRedefine parent method in child
MRODetermines order of method calls
Types of InheritanceSingle, Multiple, Multilevel, Hierarchical, Hybrid

🧩 Final Thoughts

Inheritance is a powerful and essential tool in Python, enabling code reuse and logical structuring. However, with great power comes great responsibility – use it wisely. Remember that composition over inheritance is often a better design choice, especially in complex systems.


❓ FAQ: Inheritance in Python

1. What is inheritance in Python?

Inheritance is a feature in Python that allows one class (child or subclass) to inherit attributes and methods from another class (parent or superclass), promoting code reuse and logical structure.


2. Why should I use inheritance?

  • To avoid duplicating code
  • To build hierarchical class structures
  • To extend or customize the functionality of an existing class

3. What does self mean in Python classes?

self represents the instance (object) of the class. It is used to access variables and methods that belong to the object.


4. What is the __init__ method used for?

__init__() is a special method called a constructor. It runs automatically when you create a new object and is used to initialize the object’s data (attributes).


5. How do I call the parent class constructor in Python?

Use the super() function:

super().__init__()

This is commonly used inside the child class’s __init__() method to initialize the parent class.


6. What happens if I don’t use super() in the child class?

If you override the constructor (__init__) in a child class and don’t call super(), the parent class’s constructor won’t run. This means the parent’s setup code will be skipped unless manually invoked.


7. Can a class inherit from more than one parent class?

Yes! This is called multiple inheritance. Python supports it, but be cautious and understand the Method Resolution Order (MRO) to avoid confusion.


8. What is Method Resolution Order (MRO)?

MRO defines the order in which Python looks for a method or attribute in a hierarchy involving multiple inheritance. You can view it using:

print(ClassName.__mro__)


9. What’s the difference between composition and inheritance?

  • Inheritance expresses an “is-a” relationship.
  • Composition expresses a “has-a” relationship.

For example:

  • A Car is a Vehicle → use inheritance.
  • A Car has a Engine → use composition.

10. Can I override a method in the child class?

Yes! If the child class defines a method with the same name as one in the parent class, it will override it. You can still call the parent’s version using super().


11. Are private variables inherited?

No. Attributes with double underscores (like __secret) are name-mangled and are not directly accessible in child classes. However, protected members (prefixed with a single underscore, like _var) can be accessed but should be treated with caution.


12. Can I create a class that doesn’t inherit from any other class?

Yes. In Python 3, if you don’t specify a parent class, it implicitly inherits from the base object class. For example:

class MyClass:

    pass

is equivalent to:

class MyClass(object):

    pass


13. What’s the best way to learn inheritance?

  • Start small: Create classes with simple inheritance.
  • Use super() in constructors.
  • Build small projects: e.g., users → admins → moderators.
  • Read and modify real-world code using inheritance.

14. When should I avoid inheritance?

  • When classes become too tightly coupled.
  • When multiple inheritance creates complexity.
  • Prefer composition over inheritance when you just want to use functionality rather than be the parent class.

15. What are dunder methods?

“Dunder” stands for “Double UNDerscore.” These are special methods like __init__, __str__, __len__, etc., that Python uses to define object behavior in built-in operations.

Leave a Comment