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.
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 3 syntax (recommended)
class Child(Parent):
def method(self):
super().method()
Python 2 syntax (legacy)
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
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
# 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
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:
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:
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:
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):
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
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
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
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:
| Criterion | super() | 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:
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:
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__()
# 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
# Error: super() only works inside a class
def my_function():
super() # RuntimeError: super(): no current class
Signature incompatibility in multiple inheritance
# 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
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.
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.
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.
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.