Skip to main content

Python Inheritance: Best Practices for Reusable Code

Python inheritance allows you to build new classes by reusing and extending the functionality of existing ones. Learn how to design parent-child class relationships, implement inheritance patterns, and apply techniques such as method overriding.
Feb 12, 2025  · 9 min read

Imagine you are building a software system with multiple user roles like students, teachers, and administrators. These roles share common attributes like name and ID, but they also require functionalities that are unique to them. Instead of duplicating code, inheritance allows you to define shared behavior in a parent class and extend it in specialized child classes.

In this article, we will explore Python inheritance, covering both basic and advanced concepts such as method overriding and the super() function, which is a built-in function that returns a temporary object of the superclass, so you can access its methods without explicitly naming the parent class. Don't worry if that doesn't make sense yet because we will learn all about this below.

Learn Python From Scratch

Master Python for data science and gain in-demand skills.
Start Learning for Free

Basics of Python Inheritance

Inheritance is one of the foundational pillars of object-oriented programming (OOP) that allows one class (called the child class) to derive attributes and methods from another class (called the parent class). This feature is central to code reuse and simplifies maintenance, making it easier to build scalable and efficient programs.

Defining parent and child classes

Before going further, let's explore the relationship between parent and child classes.

Parent class

Let’s start with the parent class. A parent class is the base class from which child classes derive. It encapsulates shared attributes and methods.

Using Python, here is how we define a parent class:

class ParentClass:
    def __init__(self, attributes):
        # Initialize attributes
        pass

    def method(self):
        # Define behavior
        pass

Child class 

A child class inherits attributes and methods from the parent class. This allows it to use the functionality defined in the parent class. The following code shows how a child class inherits attributes and methods from a parent class:

class ChildClass(ParentClass):
    def additional_method(self):
        # Define new behavior
        pass

This simple syntax allows the child class to utilize and extend the functionality defined in the parent class.

Creating a parent class and a child class

Let’s create a practical example with a Person class as the parent and a Student class as the child.

Creating the parent class

The Person class contains shared attributes and a method to display information:    

# Defining the Parent Class
class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id
    def display_info(self):
        return f"Name: {self.name}, ID: {self.id}"

Creating the child class

The Student class inherits from Person and adds a new method study.

# Defining the Child Class
class Student(Person):
    def study(self):
        return f"{self.name} is studying."

Let’s test the parent and child classes.

# Creating and Testing Instances
student = Student("Samuel", 102)
print(student.display_info())  
print(student.study())      
Name: Samuel, ID: 102
Samuel is studying.

Here’s what’s happening:

  1. The Student class uses the __init__ method from Person to initialize name and id.

  2. The study method is unique to the Student class, extending its functionality.

  3. The display_info method is inherited directly from Person.

Types of Inheritance in Python

Inheritance in Python allows classes to inherit attributes and behaviors from other classes, promoting code reuse and clean design, as we talked about earlier. In this section, we can talk about the different types of Python inheritance, which includes single, multiple, hierarchical, and hybrid inheritance as separate categories.

Single inheritance

Single inheritance occurs when a child class inherits from a single parent class, allowing it to extend the functionality of the parent. This is useful when an object type shares common properties with a broader category but also requires additional attributes or behavior.

The example I started to work through earlier was single inheritance, but let's now look a bit more closely: In a school management system, all individuals, including students, teachers, and staff, share some common details like name and ID. However, students also have academic records such as grades and enrolled courses. Using single inheritance, we can create a Person class for shared attributes and extend it with a Student class for academic details.

single inheritance

Single inheritance. Image by Author

Here’s a good example of the above scenario:

class Person:
    """Represents a general person with basic details."""
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    """Represents a student, extending the Person class to include academic details."""
    def __init__(self, name, id, grade, courses):
        super().__init__(name, id)
        self.grade = grade
        self.courses = courses

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}, Courses: {', '.join(self.courses)}"

# Example usage in a school system
student = Student("Samuel", 5678, "B+", ["Math", "Physics", "Computer Science"])
print(student.get_details())
Name: Samuel, ID: 5678, Grade: B+, Courses: Math, Physics, Computer Science

The Student class inherits the get_details() method from Person but extends it to include grade and courses. This is a good example of how single inheritance promotes what is known as modular code.

Multiple inheritance

Multiple inheritance, like a family tree, in a way, allows a child class to inherit from more than one parent class, combining attributes and behaviors from each. This can lead to potential conflicts, which Python resolves using method resolution order (MRO).

multiple inheritance

Multiple inheritance. Image by Author

Take a look:

class Person:
    def get_details(self):
        return "Details of a person."

class Athlete:
    def get_skill(self):
        return "Athletic skills."

class Student(Person, Athlete):
    pass

# Example usage
student = Student()
print(student.get_details())
print(student.get_skill())
Details of a person.
Athletic skills.

We see that the Student class inherited attributes and methods from both Person and Athlete. Without any additional effort, the Student class has access to the get_details()  method from the Person parent class and the get_skill() method from the Athlete parent class. We are effectively combining functionality from multiple sources.

However, inheriting from multiple classes can lead to conflicts. What if both parent classes define a method or attribute with the same name? I mentioned something about method resolution order earlier but let me know say a little something more about it. Method resolution order determines the order in which classes are searched for methods and attributes. The MRO follows a depth-first, left-to-right approach.

You can view the MRO of a class using the __mro__ attribute or the mro() method:

print(Student.__mro__)
(<class '__main__.Student'>, <class '__main__.Person'>, <class '__main__.Athlete'>, <class 'object'>)

Multilevel, hierarchical, and hybrid inheritance

Python also supports more complex inheritance structures. I'll show these more complex ideas using the same example.

Multilevel inheritance

Multilevel inheritance happens when a child class inherits from another child class, and that child class inherits from a parent class. This creates a chain of inheritance.

multilevel inheritance

Multilevel inheritance. Image by Author

Here’s a good example:

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

class GraduateStudent(Student):
    def __init__(self, name, id, grade, thesis_title):
        super().__init__(name, id, grade)
        self.thesis_title = thesis_title

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}, Thesis: {self.thesis_title}"

# Example usage
grad_student = GraduateStudent("Charlie", 91011, "A", "AI in Healthcare")
print(grad_student.get_details())
Name: Charlie, ID: 91011, Grade: A, Thesis: AI in Healthcare

Here, each class in the chain adds something new: Person manages names and IDs, Student includes grades, and GraduateStudent introduces a thesis. Thanks to super().__init__(), we reuse the initialization logic without duplicating code. It’s efficient, neat, and ensures every level of the “inheritance ladder”, as I think of it, works.

Hierarchical inheritance

In hierarchical inheritance, multiple child classes inherit from a single parent class, allowing for shared behavior across subclasses with unique attributes.

hierarchical inheritance

Hierarchical inheritance. Image by Author

Let’s look at a good example together:

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

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

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Subject: {self.subject}"

# Example usage
student = Student("Samuel", 5678, "B+")
teacher = Teacher("Dr. Smith", 1234, "Math")
print(student.get_details())  
print(teacher.get_details())
Name: Samuel, ID: 5678, Grade: B+
Dr. Smith, ID: 1234, Subject: Math

Here, the Person class serves as the foundation, offering common attributes and methods (name, id, and get_details). The Student and Teacher classes then extend this functionality by adding their unique properties (grade and subject) and customizing the get_details method to reflect their specific contexts.

With this approach, shared functionality stays in one place (the Person class), while specialized behavior is neatly encapsulated in the subclasses.

Hybrid inheritance

Hybrid inheritance combines multiple inheritance types, such as multilevel or multiple inheritance, to model more complex relationships.

hybrid inheritance

Hybrid inheritance. Image by Author

Let’s look at an example that shows the complexity of hybrid inheritance.

# Base class
class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

# Intermediate class inheriting from the base class
class Employee(Person):
    def __init__(self, name, id, position):
        super().__init__(name, id)
        self.position = position

    def get_position(self):
        return f"Position: {self.position}"

# Another independent base class
class Athlete:
    def __init__(self, sport):
        self.sport = sport

    def get_sport(self):
        return f"Sport: {self.sport}"

# Derived class combining Employee and Athlete
class Student(Employee, Athlete):
    def __init__(self, name, id, position, grade, sport):
        Employee.__init__(self, name, id, position)
        Athlete.__init__(self, sport)
        self.grade = grade

    def get_grade(self):
        return f"Grade: {self.grade}"

# Example usage
student = Student("Samuel", 1234, "Intern", "A", "Soccer")
print(student.get_details())  # From Person
print(student.get_position())  # From Employee
print(student.get_grade())  # From Student
print(student.get_sport())  # From Athlete
Name: Samuel, ID: 1234
Position: Intern
Grade: A
Sport: Soccer

In this example, the Student class demonstrates hybrid inheritance by inheriting attributes and methods from both Employee (which itself inherits from Person) and Athlete. This combines hierarchical inheritance (where Employee inherits from Person) and multiple inheritance (where Student inherits from both Employee and Athlete).

Benefits of Inheritance in Python

Now, it's time to see the strengths and weaknesses: 

Benefits of inheritance

  1. Reusability: With inheritance you can write code once in the parent class and reuse it in the child classes. Using the example, both FullTimeEmployee and Contractor can inherit a get_details() method from the Employee parent class.

  2. Simplicity: Inheritance models relationships clearly. A good example is the FullTimeEmployee class which “is-a” type of the Employee parent class.

  3. Scalability: It also add new features or child classes without affecting existing code. For example, we can easily add a new Intern class as a child class.

Potential limitations of inheritance

  1. Complexity: This won't be surprising, but too many levels of inheritance can make the code hard to follow. For example, if an Employee has too many child classes like Manager, Engineer, Intern, etc., it may become confusing.

  2. Dependency: Changes to a parent class can unintentionally affect all subclasses. If you modify Employee for example, it might break FullTimeEmployee or Contractor.

  3. Misuse: Using inheritance when it is not the best fit can complicate designs. You would not want to create a solution where Car inherits from Boat just to reuse move(). The relationship doesn’t make sense.

Advanced Python Inheritance Techniques

Now that we have explored the basics of inheritance, let’s look at some advanced techniques. These techniques, like method overriding, super(), abstract base classes, and polymorphism, enhance code flexibility and allow for more sophisticated design patterns. 

Overriding methods in python

Method overriding allows a child class to provide a specific implementation for a method already defined in its parent class. This is useful when the inherited behavior doesn’t fully meet the requirements of the child class.

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id
    
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}"

class Student(Person):
    def __init__(self, name, id, grade):
        super().__init__(name, id)
        self.grade = grade
    
    # Overriding the get_details method
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

# Example usage
student = Student("Samuel", 1234, "A")
print(student.get_details())
Name: Samuel, ID: 1234, Grade: A

Here, the Student class overrides the get_details() method from the Person class to give its own specific implementations. This allows the child class to have its own behavior while still following the same method name.

So why do we override? We override because we want to customize inherited behavior and also because we want to tailor the functionality of a parent method to a child class’s unique requirements.

Using super() for parent initialization

The super() function is used to call methods in the parent class from the child class. This is particularly useful when you want to extend or modify the functionality of a parent class method, such as the __Init__() constructor method.

So why do we use the super() function? We use the super function because we want to call and initialize the parent class’s constructor and also because we want to avoid explicitly naming the parent class. This is helpful, especially in cases of multiple inheritance.

class Person:
    def __init__(self, name, id):
        self.name = name
        self.id = id

class Student(Person):
    def __init__(self, name, id, grade):
        # Using super() to initialize the parent class
        super().__init__(name, id)
        self.grade = grade

# Example usage
student = Student("Samuel", 5678, "B+")
print(student.name)
print(student.id)
print(student.grade)
Samuel
5678
B+

Here, the Student class uses super().__init__(name, id) to call the __init__ method of the Person parent class, so it does not need to repeat code to initialize the name and id attributes. The child class then introduces a grade attribute, which is specific to the Student class.

Abstract base classes (ABCs)

An abstract base class (ABC) is a class that cannot be directly used to create objects. It is meant to define a common set of methods that other classes should implement. So ABCs are useful when you want to ensure that certain methods are always present in the child classes.

from abc import ABC, abstractmethod

class Person(ABC):
    @abstractmethod
    def get_details(self):
        pass

class Student(Person):
    def __init__(self, name, id, grade):
        self.name = name
        self.id = id
        self.grade = grade
    
    def get_details(self):
        return f"Name: {self.name}, ID: {self.id}, Grade: {self.grade}"

# Example usage
student = Student("Hamilton", 7890, "A-")
print(student.get_details())
Name: Hamilton, ID: 7890, Grade: A-

The Person class here is an abstract class that requires any child class to implement the get_details() method. This method is being later implemented by the child class, Student.

Polymorphism

Polymorphism means many shapes. In Python, it allows different classes to use the same method name, but each can implement that method in a different way.

Polymorphism helps us write code that can work with objects of different classes, even if those classes have different behaviors:

class Person:
    def get_details(self):
        return "Details of a person."

class Student(Person):
    def get_details(self):
        return "Details of a student."

class Teacher(Person):
    def get_details(self):
        return "Details of a teacher."

# Example usage
def print_details(person):
    print(person.get_details())

student = Student()
teacher = Teacher()

print_details(student) 
print_details(teacher) 
Details of a student.
Details of a teacher.

In this example, the function print_details() can accept any object of type Person, but it will call the appropriate get_details() method based on whether the object is a Student or a Teacher.

Common Mistakes and Best Practices

While inheritance is powerful, it is easy to misuse. I will share some ideas to help you make the most of the ideas.

Avoiding surprises with overridden methods

When a child class overrides a method from its parent, the behavior can change.

For example, if the parent class Employee has a calculate_pay() method, and the Manager child class overrides it without considering all scenarios, it might produce incorrect pay calculations.

The best practice, in this case, is to always test overridden methods thoroughly and document their behavior.

Choosing between inheritance and composition

I know this article is about inheritance, but it is not always the right approach. Sometimes, composition, where you build classes by combining objects rather than extending them might be a better fit with whatever you are doing.

To distll the differences in the most basic way, think that:

  • Inheritance refers to “Is-a” relationships. For example, a Manager is an Employee.

  • Composition refers to “Has-a” relationships. For example, a Car has an Engine.

So, how do you know when composition is the best approach to use? Use composition when the relationship is not strictly hierarchical and/or when you want to reduce tight coupling between classes.

Or, we could also say that, while inheritance models relationships, composition focuses on functionality. To help, consider this:

  • Use inheritance when objects are naturally hierarchical. For example, Animal > Bird > Parrot.

  • Use composition when objects share functionality but are not related. For example, a Printer and Scanner both use a DeviceManager.

Avoid deep inheritance chains

Deep inheritance chains (many levels of parent-child relationships) can make your code hard to read and maintain. This is a problem because changes to a parent class may unintentionally affect many child classes. Also, debugging becomes complex as behavior is spread across multiple levels.

The best practice in this case is to keep hierarchies shallow. Also, consider using composition (as I mentioned earlier) or breaking a chain into separate hierarchies if you find it is becoming too deep.

Conclusion

Inheritance is a major pillar of object-oriented programming that enables developers like you to create reusable, modular, and scalable code. If you can master inheritance, you will find it easy to simplify complex systems.

A good way to deepen your understanding is to try building inheritance structures in your projects. Start simple, then experiment with more complex hierarchies to see how they work in practice.

If you are eager to explore even deeper, you can check out our Programming Paradigm Concepts course for a deeper understanding of inheritance and other ideas. Our Python Developer career track is also a good resource that offers a comprehensive path to developing advanced programming skills that will equip you for software development.


Samuel Shaibu's photo
Author
Samuel Shaibu
LinkedIn

Experienced data professional and writer who is passionate about empowering aspiring experts in the data space.

Python Inheritance FAQs

What is inheritance in Python?

Inheritance in Python is a mechanism that allows a class to inherit attributes and methods from another class, promoting code reusability and hierarchical class structures.

How does Python inheritance work?

Python inheritance works by defining a new class that inherits attributes and methods from an existing class, allowing for hierarchical class structures.

What are the types of inheritance in Python?

Python supports several types of inheritance, including single, multiple, multilevel, and hierarchical inheritance.

What is the difference between single and multiple inheritance in Python?

Single inheritance involves one parent class, while multiple inheritance allows a class to inherit from multiple parent classes.

How do you implement inheritance in Python?

Implement inheritance by defining a new class that specifies a parent class in its definition, using the syntax class ChildClass(ParentClass):.

What are the benefits of using inheritance in Python?

Inheritance promotes code reusability, allows for the creation of hierarchical class structures, and supports polymorphism, making code more flexible and maintainable.

How do you implement multiple inheritance in Python?

Multiple inheritance is implemented by defining a class that inherits from more than one base class, with Python handling method resolution using the method resolution order (MRO).

Topics
Related
Data Skills

blog

6 Python Best Practices for Better Code

Discover the Python coding best practices for writing best-in-class Python scripts.
Javier Canales Luna's photo

Javier Canales Luna

13 min

tutorial

Python Abstract Classes: A Comprehensive Guide with Examples

Learn about Python abstract classes, their purpose, and how to use the `abc` module to enforce consistent interfaces. Includes practical examples and best practices for effective implementation.
Derrick Mwiti's photo

Derrick Mwiti

10 min

tutorial

Intro to Multiple Inheritance & super()

A Pythonista's introductory guide to multiple inheritance, the super() function, & how to navigate the diamond problem.
Adam Spannbauer's photo

Adam Spannbauer

8 min

tutorial

Object-Oriented Programming in Python (OOP): Tutorial

Tackle the basics of Object-Oriented Programming (OOP) in Python: explore classes, objects, instance methods, attributes and much more!
Théo Vanderheyden's photo

Théo Vanderheyden

12 min

tutorial

How to Document Python Code

Learn why there is a need for documenting code and best practices to do it. Further, learn to leverage the potential of the Pydoc module for documenting purposes.
Aditya Sharma's photo

Aditya Sharma

14 min

tutorial

Coding Best Practices and Guidelines for Better Code

Learn coding best practices to improve your programming skills. Explore coding guidelines for collaboration, code structure, efficiency, and more.
Amberle McKee's photo

Amberle McKee

26 min

See MoreSee More