What is super() in Python? Complete guide

Discover the super() function in Python: how to call parent class methods, handle multiple inheritance, and write clean object-oriented code.
8 min read
Believemy logo

Definition of super() in Python

The super() function is a built-in Python function that allows you to access the methods and attributes of a parent class (or base class) from a child class. It plays a fundamental role in the inheritance mechanism, one of the pillars of object-oriented programming (OOP).

Concretely, when you create a class that inherits from another, super() allows you to call the parent class methods without having to name it explicitly. This makes your code more flexible, more maintainable, and better suited for complex architectures.

If you want to master object-oriented programming in Python and all its subtleties, our complete Python course will guide you step by step through this learning process.

Good to know

super() returns a temporary proxy object that delegates method calls to the parent class (or the next class in the method resolution order, the MRO).

 

Basic syntax of super()

The syntax of super() has evolved with Python versions. Since Python 3, it has become much simpler:

PYTHON
class Child(Parent):
    def method(self):
        super().method()

 

Python 2 syntax (legacy)

PYTHON
class Child(Parent):
    def method(self):
        super(Child, self).method()

In Python 3, you no longer need to pass the class and instance as arguments. Python does it automatically for you. However, the syntax with arguments remains valid and can be useful in certain advanced cases.

 

Using super() with __init__

The most common use case for super() is calling the __init__ constructor of the parent class. This allows you to initialize inherited attributes before adding attributes specific to the child class.

Simple inheritance example

PYTHON
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce(self):
        return f"{self.name} is {self.age} years old."


class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)  # Call to parent constructor
        self.breed = breed

    def introduce(self):
        base = super().introduce()  # Call to parent method
        return f"{base} It's a {self.breed}."


my_dog = Dog("Rex", 5, "German Shepherd")
print(my_dog.introduce())
# Rex is 5 years old. It's a German Shepherd.

In this example, the class Dog inherits from Animal. Thanks to super().__init__(name, age), we don't need to duplicate the initialization code for name and age. The child class focuses only on what is specific to it: the breed attribute.

 

Without super(): what you should not do

PYTHON
# Bad practice: direct call to the parent class
class Dog(Animal):
    def __init__(self, name, age, breed):
        Animal.__init__(self, name, age)  # Works, but not recommended
        self.breed = breed
Warning

Calling Animal.__init__(self, ...) directly works in simple cases, but causes problems in multiple inheritance. Always use super() to ensure correct behavior.

 

super() and multiple inheritance

It is in the context of multiple inheritance that super() reveals its full power. Python uses an algorithm called MRO (Method Resolution Order) to determine the order in which parent classes are traversed.

The diamond problem

The "diamond problem" occurs when a class inherits from two classes that themselves inherit from the same base class:

PYTHON
class A:
    def __init__(self):
        print("A.__init__")
        self.value = "A"


class B(A):
    def __init__(self):
        print("B.__init__")
        super().__init__()
        self.value_b = "B"


class C(A):
    def __init__(self):
        print("C.__init__")
        super().__init__()
        self.value_c = "C"


class D(B, C):
    def __init__(self):
        print("D.__init__")
        super().__init__()
        self.value_d = "D"


d = D()
# D.__init__
# B.__init__
# C.__init__
# A.__init__

Thanks to super() and the MRO, each constructor is called only once, and in the correct order. Without super(), the constructor of A could have been called twice.

 

Checking the MRO of a class

You can inspect the method resolution order with the mro() method or the __mro__ attribute:

PYTHON
print(D.mro())
# [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]

# Or with __mro__
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

The MRO follows the C3 Linearization algorithm, which guarantees a consistent and predictable order. super() follows this order to delegate method calls.

 

Advanced practical examples

Overriding a method while preserving parent behavior

A very common case consists of extending the behavior of a parent method rather than replacing it entirely:

PYTHON
class File:
    def save(self, data):
        print(f"Saving {len(data)} characters...")
        self.content = data
        return True


class FileWithLog(File):
    def __init__(self):
        super().__init__()
        self.history = []

    def save(self, data):
        # Add behavior before
        self.history.append(f"Save: {len(data)} characters")
        print(f"[LOG] Attempting to save...")

        # Call parent behavior
        result = super().save(data)

        # Add behavior after
        if result:
            print(f"[LOG] Save successful.")
        return result


file = FileWithLog()
file.save("Hello world")
# [LOG] Attempting to save...
# Saving 11 characters...
# [LOG] Save successful.

 

Using super() with properties

You can also use super() with Python properties (the @property decorator):

PYTHON
class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"


class Doctor(Person):
    @property
    def full_name(self):
        return f"Dr. {super().full_name}"


doc = Doctor("John", "Smith")
print(doc.full_name)
# Dr. John Smith

 

super() in class methods and static methods

PYTHON
class Base:
    counter = 0

    @classmethod
    def increment(cls):
        cls.counter += 1
        print(f"Base.counter = {cls.counter}")


class Derived(Base):
    @classmethod
    def increment(cls):
        super().increment()  # Also works with @classmethod
        print(f"Increment from Derived")


Derived.increment()
# Base.counter = 1
# Increment from Derived

 

super() with arguments: advanced cases

Although the syntax without arguments is preferred in Python 3, the version with arguments remains useful in certain situations:

Accessing a specific class in the hierarchy

PYTHON
class A:
    def greet(self):
        return "Hello from A"


class B(A):
    def greet(self):
        return "Hello from B"


class C(B):
    def greet(self):
        return "Hello from C"

    def greet_from_a(self):
        # Skip B and go directly to A
        return super(B, self).greet()


c = C()
print(c.greet())              # Hello from C
print(c.greet_from_a())       # Hello from A
Warning

Skipping classes in the hierarchy with super(SpecificClass, self) is an advanced technique. Use it with caution as it can make the code difficult to maintain.

 

Comparison: super() vs direct call

Here is a comparison table to understand why super() is preferable to directly calling the parent class:

Criterionsuper()Direct call (Parent.method())
Simple inheritance✅ Works perfectly✅ Works too
Multiple inheritance✅ Correctly handles MRO❌ Risk of duplicates
Refactoring✅ No need to change the name❌ Hardcoded class name
Maintainability✅ Decoupled code❌ Tight coupling
Readability✅ Standard convention⚠️ Can be confusing

 

Best practices with super()

To use super() effectively, here are the rules to follow:

1. Always use super() rather than a direct call

Even if your class has only one parent, use super(). Your code will be ready for future evolution.

 

2. Call super().__init__() at the beginning of the constructor

As a general rule, initialize the parent class first before adding your own attributes:

PYTHON
class MyWidget(WidgetBase):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)  # Parent first
        self.customized = True      # Then your attributes

 

3. Use **kwargs for compatibility in multiple inheritance

In multiple inheritance, each class may need different parameters. Use **kwargs to pass arguments through:

PYTHON
class A:
    def __init__(self, param_a=None, **kwargs):
        super().__init__(**kwargs)
        self.param_a = param_a


class B:
    def __init__(self, param_b=None, **kwargs):
        super().__init__(**kwargs)
        self.param_b = param_b


class C(A, B):
    def __init__(self, param_c=None, **kwargs):
        super().__init__(**kwargs)
        self.param_c = param_c


# All parameters are correctly distributed
obj = C(param_a="a", param_b="b", param_c="c")
print(obj.param_a, obj.param_b, obj.param_c)  # a b c

 

4. Do not mix super() and direct calls

In a class hierarchy, if some classes use super() and others do not, the MRO may not be respected correctly. Be consistent.

 

5. Document the inheritance chain

Use docstring to explain why you call super() and which arguments are passed, especially in multiple inheritance.

 

Common errors with super()

Forgetting to call super().__init__()

PYTHON
# Error: parent attributes are not initialized
class Child(Parent):
    def __init__(self):
        # Forgot super().__init__()
        self.child_attribute = "value"

# Result: AttributeError when accessing parent attributes

 

Calling super() outside a class

PYTHON
# Error: super() only works inside a class
def my_function():
    super()  # RuntimeError: super(): no current class

 

Signature incompatibility in multiple inheritance

PYTHON
# Problem: incompatible signatures
class A:
    def __init__(self, x):
        self.x = x

class B:
    def __init__(self, y):
        self.y = y

class C(A, B):
    def __init__(self):
        # How to pass the right arguments?
        super().__init__(x=1)  # B never receives y!

# Solution: use **kwargs as shown above

 

Frequently asked questions

Question

What is the difference between super() and the parent class name?

super() uses the MRO (Method Resolution Order) to determine which parent class to call, which is essential in multiple inheritance. The direct call (Parent.method(self)) ignores the MRO and always calls the same class, which can cause multiple or missing calls in complex hierarchies. In summary, super() is safer, more flexible, and is the practice recommended by the Python community.

 

Question

Can you use super() without inheritance?

Technically, every Python class implicitly inherits from object. So super() will work, but it will call the methods of object, which is rarely useful. In practice, super() only makes sense when your class explicitly inherits from another class with methods you want to reuse or extend.

 

Question

Does super() work with magic methods (dunder methods)?

Yes, super() works with all methods, including magic methods like __init__, __str__, __repr__, __eq__, etc. This is actually its most common usage: calling the parent __init__ to initialize inherited attributes. You can also use it with __new__ to control instance creation.

 

Question

How can I learn to use super() and OOP in Python properly?

Mastering super() requires a good understanding of object-oriented programming: inheritance, polymorphism, encapsulation. We recommend following our dedicated Python course on Believemy, which covers inheritance, classes, and all the subtleties of super() in depth with hands-on exercises and real-world projects.

Related terms

Discover our python glossary

Browse the terms and definitions most commonly used in development with Python.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.