Skip to main content

How super() Works in Python

Python · OOP · Inheritance

How super() Works in Python

A practical guide to inheritance, method overriding, method resolution order, and the binding rules behind super().

Beginner view

super() helps a subclass reuse behavior from a class above it instead of rewriting everything.

What matters most

It follows Python’s method resolution order, which becomes especially important in multiple inheritance.

Missing detail

The zero-argument form of super() is specially bound by Python, which is why it behaves differently from super(cls).

Introduction

In object-oriented Python, inheritance lets one class build on another. That is useful, but it also creates a common problem: when a subclass changes a method, how do you keep the useful behavior from the class above it?

That is where super() comes in.

This guide explains what super() does, how it works in constructors and ordinary methods, why it matters in multiple inheritance, and what Python is doing behind the scenes when you call it.

Core idea

What super() Means

In plain English, super() gives you a way to continue method lookup through the class hierarchy instead of hardcoding a specific parent class name.

Many beginners hear this described as “calling the parent class,” which is close enough to get started, but not quite the full story.

The beginner version super() lets a subclass reuse behavior from a class above it.

The real version super() follows Python’s method resolution order, or MRO, which is especially important in multiple inheritance.
Constructors

Using super() in a Constructor

One of the most common uses of super() is inside __init__. This lets a subclass keep the parent’s initialization logic and then add its own.

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


class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed

Here, Dog reuses the Animal setup for name, then adds its own breed attribute.

Without super(), you would either duplicate logic or call Animal.__init__(self, name) directly. That direct call works in simple cases, but it is less flexible and becomes a problem in cooperative multiple inheritance.

Overriding

Using super() When Overriding a Method

super() is also useful when a subclass wants to extend a method rather than replace it completely.

class Employee:
    def describe(self):
        return "Employee record"


class Manager(Employee):
    def describe(self):
        base_description = super().describe()
        return f"{base_description} | Manager privileges enabled"

In this example, Manager.describe() keeps the parent behavior and adds something new.

That is a good use of super().

Important Do not call super() automatically every time you override a method. Use it only when the parent behavior is still part of the correct result.
When not to use it

When You Should Not Use super()

Sometimes a subclass is not extending a method. It is replacing it entirely.

class Shape:
    def area(self):
        raise NotImplementedError("Subclasses must implement area()")


class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

Here, Rectangle.area() should not call super().area(). The parent method is only a placeholder. There is no useful parent behavior to preserve.

This is a better teaching example than showing super().area() on a method that does nothing.

Multiple inheritance

Why super() Matters in Multiple Inheritance

This is where super() becomes much more important.

With multiple inheritance, Python needs a consistent order for searching methods. That order is called the method resolution order, or MRO.

Consider this example:

class A:
    def show(self):
        print("A")


class B(A):
    def show(self):
        print("B")
        super().show()


class C(A):
    def show(self):
        print("C")
        super().show()


class D(B, C):
    pass


d = D()
d.show()

The output is:

B
C
A

The important point is that super() does not simply mean “call my immediate parent.” It means “continue lookup using Python’s MRO.”

For D, Python’s MRO is:

D → B → C → A

So when B.show() calls super().show(), the next method is C.show(), not A.show().

That is the key idea many beginner explanations leave out.

Binding mechanics

How super() Is Actually Bound

Once the MRO idea is clear, the next detail is how super() gets bound to the current method call. Python supports two main forms:

super()
super(type, obj_or_type)

The zero-argument form is the one most people use. Inside a normal method, Python fills in the needed context automatically.

class Base:
    def greet(self):
        return "hello"


class Child(Base):
    def greet(self):
        return super().greet()

In a method like that, super() behaves like a properly bound lookup that knows both the current class and the current object.

Key idea Zero-argument super() gets special help from Python based on the method context. That is why it feels simpler than the explicit form.

The explicit form makes the two pieces visible:

super(CurrentClass, obj)

The first argument tells Python where to start in the MRO. The second argument provides the object, or in some cases a class, that the lookup should be bound to.

That is why this matters:

super(cls)

That call only provides the first piece. It does not automatically provide the bound object or bound class for you. So it is not the same as:

super(CurrentClass, cls)

Those are different calls with different behavior. The first is incomplete for the usual bound lookup you want. The second supplies both arguments explicitly.

Common confusion super(cls) does not mean “use the current method’s cls as the second argument too.” Only zero-argument super() gets that special automatic binding behavior.
Direct calls

Why Not Just Call the Parent Class Directly?

You could write this:

Animal.__init__(self, name)

But hardcoding the parent class name makes your code more rigid.

It ties the method to one specific class and ignores the cooperative lookup order Python uses in more complex hierarchies. In simple single inheritance, this may still work. In multiple inheritance, it can break method cooperation badly.

super() is preferred because it keeps the hierarchy more flexible.

Best practices

Best Practices for super()

  • Use super() in __init__ when a parent class has important initialization logic.
  • Use it when you want to extend a parent method, not replace it entirely.
  • Remember that super() follows the MRO, not just one parent class name.
  • Do not treat it like a ritual. Call it because the parent behavior is needed, not because inheritance exists.
  • In multiple inheritance, design methods to cooperate with super().
  • Do not assume super(cls) is the same as zero-argument super().
FAQ

Frequently Asked Questions

These are the practical questions people usually have when they first start using super() seriously in Python.

Is super() just “call the parent”?

Not exactly. That description is close enough to get started, but the more accurate idea is that super() continues method lookup through Python’s method resolution order.

Why is super() common in __init__?

Because subclasses often need to keep important initialization logic from a class above them and then add their own setup afterward.

Should I use super() every time I override a method?

No. Use it when the parent behavior is still part of the correct behavior. Do not use it when the subclass should replace the method entirely.

Why not just call the parent class directly?

Direct parent calls make code more rigid and can break cooperative behavior in multiple inheritance. super() keeps method lookup more flexible.

What is the MRO in simple terms?

The MRO is the order Python uses to search classes for methods and attributes. It matters because super() follows that order.

Why does super() matter more in multiple inheritance?

Because with multiple inheritance, there may be several classes in the hierarchy, and Python needs one consistent order for method lookup and cooperation.

Can super() be useful outside constructors?

Yes. It is often useful when overriding ordinary methods and extending parent behavior instead of replacing it.

What is the safest way to think about super()?

Think of it as “continue lookup through the hierarchy” rather than “jump to one specific parent.”

Is super(cls) the same as super(CurrentClass, cls)?

No. super(cls) supplies only the first argument. It does not automatically bind the second argument for you. Zero-argument super() is the form that gets special automatic binding from Python.

What are the two forms of super()?

The common forms are super() and super(type, obj_or_type). The first is shorthand with special compiler help. The second is the explicit form where you provide the binding context yourself.

Conclusion

super() is one of the most useful tools in Python’s object model, but it is often explained too loosely. It is not just a shortcut for calling a parent method. It is Python’s way of continuing method lookup through the class hierarchy in a consistent way.

In simple inheritance, that often looks like reusing a parent constructor or extending a parent method. In multiple inheritance, it becomes the mechanism that makes cooperative method calls possible.

The binding detail matters too. Zero-argument super() is not just shorter syntax. It is a specially supported form that knows the current method context. That is why it behaves differently from incomplete explicit forms like super(cls).

The safest way to think about it is this:

  • use super() when the parent behavior is still part of the correct behavior
  • do not use it when the subclass should replace the method completely
  • remember that super() continues lookup through the MRO, and that the zero-argument form is specially bound by Python

Once that distinction clicks, super() becomes much easier to use correctly.

Raell Dottin

Comments