Skip to main content
HomeAbout PythonLearn Python

Intro to Multiple Inheritance & super()

A Pythonista's introductory guide to multiple inheritance, the super() function, & how to navigate the diamond problem.
Feb 2019  · 8 min read

Quick overview of inheritance

As you grow your Python projects and packages, you'll inevitably want to utilize classes and apply the DRY (don't-repeat-yourself) principle while doing so. Class inheritance is a fantastic way to create a class based on another class in order to stay DRY. This post will cover more advanced concepts of inheritance, and basic inheritance won't be covered in depth. We'll go over a quick intro, but there are much better, detailed introductions out there. Here are some resources to get started: Object-Oriented Programming in Python course & Python Object-Oriented Programming (OOP): Tutorial.

So what is class inheritance? Similarly to genetics, a child class can 'inherit' attributes and methods from a parent. Let's jump right into some code for an example. In the below code block we'll demonstrate inheritance with a Child class inheriting from a Parent class.


class Parent:
    def __init__(self):
        self.parent_attribute = 'I am a parent'

    def parent_method(self):
        print('Back in my day...')

# Create a child class that inherits from Parent
class Child(Parent):
    def __init__(self):
        self.child_attribute = 'I am a child'

# Create instance of child
child = Child()

# Show attributes and methods of child class


I am a child
I am a parent
Back in my day...

We see that the Child class 'inherited' attributes and methods from the Parent class. Without any work on our part, the Parent.parent_method is a part of the Child class. To get the benefits of the Parent.__init__() method we needed to explicitly call the method and pass self. This is because when we added an __init__ method to Child, we overwrote the inherited __init__.

With that brief, very non-comprehensive overview out of the way lets jump into the meat of the post.

Intro to super

In the simplest case, the super function can be used to replace the explicit call to Parent.__init__(self). Our intro example from the first section can be rewritten with super as seen below. Note, that the below code block is written in Python 3, earlier versions use a slightly different syntax. Additionally, the output has been omitted since it's identical to the first code block.

class Parent:
    def __init__(self):
        self.parent_attribute = 'I am a parent'

    def parent_method(self):
        print('Back in my day...')

# Create a child class that inherits from Parent
class Child(Parent):
    def __init__(self):
        self.child_attribute = 'I am a parent'

# Create instance of child
child = Child()

# Show attributes and methods of child class

To be honest, super in this case gives us little, if any, advantage. Depending on the name of our parent class we might save some keystrokes, and we don't have to pass self to the call to __init__. Below are some pros and cons of the use of super in single inheritance cases.


It can be argued that using super here makes the code less explicit. Making code less explicit violates The Zen of Python, which states, "Explicit is better than implicit."


There is a maintainability argument that can be made for super even in single inheritance. If for whatever reason your child class changes its inheritance pattern (i.e., parent class changes or there's a shift to multiple inheritance) then there's no need find and replace all the lingering references to ParentClass.method_name(); the use of super will allow all the changes to flow through with the change in the class statement.

Master your data skills with DataCamp

More than 10 million people learn Python, R, SQL, and other tech skills using our hands-on courses crafted by industry experts.

Start Learning

super and multiple inheritance

Before we get into multiple inheritance and super... Warning, this can get pretty weird and complicated.

First off, what is multiple inheritance? So far the example code has covered a single child class inheriting from a single parent class. In multiple inheritance, there's more than one parent class. A child class can inherit from 2, 3, 10, etc. parent classes.

Here is where the benefits of super become more clear. In addition to saving keystrokes of referencing the different parent class names, there are nuanced benefits to using super with multiple inheritance patterns. In short, if you're going to use multiple inheritance, use super.

Multiple inheritance without super

Let's look at an example of multiple inheritance that avoids modifying any parent methods and in turn avoids super.


class B:
    def b(self):

class C:
    def c(self):

class D(B, C):
    def d(self):

d = D()



Multiple-resolution order

This output isn't too surprising given the concept of multiple inheritance. D inherited the methods x and z from its parent classes, and everything is good in the world... for now.

So what if both B and C both had a method with the same name? This is where a concept called 'multiple-resolution order' comes into play or MRO for short. The MRO of a child class is what decides where Python will look for a given method, and which method will be called when there's a conflict.

Let's look at an example.


class B:
    def x(self):
        print('x: B')

class C:
    def x(self):
        print('x: C')

class D(B, C):

d = D()


x: B
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]

When we call the inherited x method, we only see the output inherited from B. We can see the MRO of our D class by calling the mro class method. From the D.mro() output we learn the following: our program will try to call D methods by default, then resort to B, then C, and finally object. If it's not found in any of those places, then we'll get the error that D doesn't have the method we asked for.

It's worth noting that by default, every class inherits from object, and it's on the tail-end of every MRO.

Multiple inheritance, super, and the diamond problem

Below is an example of using super to handle MRO of init in a way that's beneficial. In the example, we create a series of text processing classes and combine their functionality in another class with multiple inheritance. We'll create 4 classes, and the structure for inheritance will follow the structure in the below diagram.

Note: This structure is for illustrative purposes, and, barring restraints, there would be better ways to implement this logic.

diamond problem

This is actually an example of the 'diamond problem' of multiple inheritance. Its name is of course based on the shape of its design, and the fact that it's a fairly confusing problem.

Below the design is written out with the use of super.


class Tokenizer:
    """Tokenize text"""
    def __init__(self, text):
        print('Start Tokenizer.__init__()')
        self.tokens = text.split()
        print('End Tokenizer.__init__()')

class WordCounter(Tokenizer):
    """Count words in text"""
    def __init__(self, text):
        print('Start WordCounter.__init__()')
        self.word_count = len(self.tokens)
        print('End WordCounter.__init__()')

class Vocabulary(Tokenizer):
    """Find unique words in text"""
    def __init__(self, text):
        print('Start init Vocabulary.__init__()')
        self.vocab = set(self.tokens)
        print('End init Vocabulary.__init__()')

class TextDescriber(WordCounter, Vocabulary):
    """Describe text with multiple metrics"""
    def __init__(self, text):
        print('Start init TextDescriber.__init__()')
        print('End init TextDescriber.__init__()')

td = TextDescriber('row row row your boat')


Start init TextDescriber.__init__()
Start WordCounter.__init__()
Start init Vocabulary.__init__()
Start Tokenizer.__init__()
End Tokenizer.__init__()
End init Vocabulary.__init__()
End WordCounter.__init__()
End init TextDescriber.__init__()
['row', 'row', 'row', 'your', 'boat']
{'boat', 'your', 'row'}

First off, we see the TextDescriber class has inherited all the attributes of the class family tree. Thanks to multiple inheritance we can 'combine' the functionality of more than one class.

Let's now discuss the printouts that came from the class's init methods:

Each __init__ method was called once and only once.

The TextDescriber class inherited from 2 classes that inherit from Tokenizer. Why was Tokenizer.__init__ not called twice?

If we replaced all of our calls to super with the old fashioned way, we would end up with 2 calls to Tokenizer.__init__. The calls to super 'think' through our pattern a little bit more and skips the extra trip to A.

Each __init__ method was started before any of the others were finished.

The order of the starts and finishes of each __init__ is worth noting in case you're attempting to set an attribute that has a naming conflict with another parent class. The attribute will be overwritten, and it can become very confusing.

In our case, we avoided naming conflicts with inherited attributes, so everything is working as expected.

To reiterate, the diamond problem can get complicated fast and lead to unexpected results. With most cases in programming, it's best to avoid complicated designs.

What we learned

  • We learned about the super function and how it can be used to replace ParentName.method in single inheritance. This can be a more maintainable practice.
  • We learned about multiple inheritance and how we can pass on the functionality of multiple parent classes to a single child class.
  • We learned about multiple-resolution order and how it decides what happens in multiple inheritance when there's a naming conflict between parent methods.
  • We learned about the diamond problem and saw an example of how the use of super navigates the diamond.

Learn more about Python

Certification available


Introduction to Python

4 hr
Master the basics of data analysis with Python in just four hours. This online course will introduce the Python interface and explore popular packages.
See DetailsRight Arrow
Start Course
See MoreRight Arrow

A Deep Dive into the Phi-2 Model

Understanding the Phi-2 model and learning how to access and fine-tune it using the role-play dataset.
Abid Ali Awan's photo

Abid Ali Awan

12 min

Python List Size: 8 Different Methods for Finding the Length of a List in Python

Compare between 8 different methods for finding the length of a list in Python.
Adel Nehme's photo

Adel Nehme

8 min

An End-to-End ML Model Monitoring Workflow with NannyML in Python

Learn an end-to-end workflow to monitor any model in your Jupyter notebook in production environments.
Bex Tuychiev's photo

Bex Tuychiev

15 min

How to Delete a File in Python

File management is a crucial aspect of code handling. Part of this skill set is knowing how to delete a file. In this tutorial, we cover multiple ways to delete a file in Python, along with best practices in doing so.
Amberle McKee's photo

Amberle McKee

5 min

Finding the Size of a DataFrame in Python

There are several ways to find the size of a DataFrame in Python to fit different coding needs. Check out this tutorial for a quick primer on finding the size of a DataFrame. This tutorial presents several ways to check DataFrame size, so you’re sure to find a way that fits your needs.
Amberle McKee's photo

Amberle McKee

5 min

Exploring the Python 'Not Equal' Operator

Comparing values in Python to check if they are not equal is simple with the not equal operator. Check out this quick tutorial on how to use the not equal Python operator, as well as alternatives for comparing floats.
Amberle McKee's photo

Amberle McKee

5 min

See MoreSee More