Question
In Python, why is super() used in classes that inherit from another class? Is there a practical difference between calling Base.__init__(self) directly and using super().__init__() instead?
For example:
class Base(object):
def __init__(self):
print("Base created")
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
ChildA()
ChildB()
Both subclasses appear to initialize the base class. What is the difference, and why is super() generally recommended?
Short Answer
By the end of this page, you will understand what super() does in Python, how it helps subclasses call parent methods, and why it is usually safer and more maintainable than calling a parent class directly. You will also see how this becomes especially important in multiple inheritance.
Concept
In object-oriented Python, a subclass often wants to reuse behavior from its parent class. A common example is calling the parent class's __init__() method so that the parent can perform its own setup.
There are two main ways to do that:
Base.__init__(self)
or
super().__init__()
The key difference is this:
Base.__init__(self)explicitly calls one specific class.super().__init__()asks Python to call the next method in the method resolution order (MRO).
That makes super() more flexible and cooperative.
Why this matters
If your class hierarchy is simple and uses only single inheritance, both approaches may appear to work the same way. But in real codebases, inheritance chains can grow, and multiple inheritance can be involved. In those cases, directly naming a parent class can:
- skip other classes in the chain
- call the wrong method order
- cause duplicate initialization
- make code harder to maintain if the hierarchy changes
super() is designed to work with Python's inheritance system rather than against it.
What super() really means
Mental Model
Imagine a team relay race.
Each class in an inheritance chain is one runner. When setup work happens, the baton should be passed from one runner to the next in the correct order.
- Using
super()is like saying: pass the baton to the next runner in the official race order. - Using
Base.__init__(self)is like saying: ignore the race order and hand the baton directly to one specific runner.
In a simple two-runner race, both may seem fine. But in a bigger relay with more runners, directly choosing one runner can skip people, cause confusion, or make someone run twice.
So super() helps classes cooperate cleanly, especially when the inheritance chain is more complex.
Syntax and Examples
Core syntax
Direct parent call
class Base:
def __init__(self):
print("Base created")
class Child(Base):
def __init__(self):
Base.__init__(self)
print("Child created")
Using super()
class Base:
def __init__(self):
print("Base created")
class Child(Base):
def __init__(self):
super().__init__()
print("Child created")
Beginner-friendly example
Step by Step Execution
Trace example
class Base:
def __init__(self):
print("Base created")
class Child(Base):
def __init__(self):
print("Child setup started")
super().__init__()
print("Child setup finished")
Child()
Step by step
1. Child() is called
Python creates a new Child instance.
2. Child.__init__() runs
Execution enters the child's constructor:
print("Child setup started")
Output so far:
Child setup started
3. super().__init__() runs
Real World Use Cases
Common practical uses of super()
Initializing shared fields in parent classes
class User:
def __init__(self, username):
self.username = username
class Admin(User):
def __init__(self, username, level):
super().__init__(username)
self.level = level
This avoids duplicating username setup in every subclass.
Extending framework classes
Many Python frameworks expect subclasses to call the parent implementation.
Examples include:
- GUI widgets
- test case classes
- Django forms or views
- custom exceptions or data models
A subclass may add custom behavior but still rely on the base class to do core setup.
Cooperative multiple inheritance
Mixins and reusable class components often rely on super() so each class in the chain gets a chance to run.
:
():
()
().__init__(*args, **kwargs)
Real Codebase Usage
How developers use this in real projects
1. Extending a base class without breaking it
A subclass often adds fields or behavior but still depends on parent setup.
class BaseService:
def __init__(self, config):
self.config = config
class EmailService(BaseService):
def __init__(self, config, sender):
super().__init__(config)
self.sender = sender
2. Writing cooperative mixins
Mixins usually avoid naming parent classes directly. They call super() so they can work in different inheritance combinations.
class TimestampMixin:
def __init__(self, *args, **kwargs):
self.created_at = "now"
super().__init__(*args, **kwargs)
3. Following framework expectations
Framework base classes may perform registration, validation, resource setup, or default configuration in __init__(). If you skip , the object may be left in an invalid state.
Common Mistakes
1. Thinking super() means only "parent class"
It actually means the next class in the MRO.
That matters in multiple inheritance.
2. Hardcoding the base class name unnecessarily
Broken or inflexible style:
class Base:
def __init__(self):
print("Base")
class Child(Base):
def __init__(self):
Base.__init__(self)
Better:
class Child(Base):
def __init__(self):
super().__init__()
Why better:
- easier to maintain
- works better with inheritance changes
- supports cooperative multiple inheritance
3. Forgetting to pass arguments through
Broken example:
Comparisons
Base.__init__(self) vs super().__init__()
| Approach | What it does | Good for | Limitations |
|---|---|---|---|
Base.__init__(self) | Calls one specific class directly | Very simple cases | Tightly coupled, not cooperative, poor fit for multiple inheritance |
super().__init__() | Calls the next method in the MRO | Most modern Python class design | Requires understanding argument flow and MRO |
Single inheritance vs multiple inheritance
| Situation | Direct base call | super() |
|---|---|---|
| One parent class only |
Cheat Sheet
Quick reference
Call a parent initializer in Python 3
super().__init__()
Pass arguments to the parent
super().__init__(name, age)
Old explicit form
super(MyClass, self).__init__()
Rules of thumb
- Prefer
super()over direct parent class calls - Use it in subclasses that extend parent behavior
- Remember:
super()follows the MRO, not just the immediate parent - In multiple inheritance, all participating classes should use
super()cooperatively - Forward
*argsand**kwargswhen building flexible mixins
Common patterns
Extend parent setup
class Child(Base):
def __init__():
().__init__()
.value =
FAQ
What does super() do in Python?
It returns a proxy that lets you call the next method in the class's method resolution order.
Is super().__init__() the same as Base.__init__(self)?
Not exactly. In simple single inheritance, they may behave similarly. But super() follows the MRO, while Base.__init__(self) directly targets one class.
Why is super() recommended in Python?
Because it is more maintainable, supports cooperative multiple inheritance, and adapts better if the class hierarchy changes.
Should I always use super() in __init__()?
Usually yes when extending a parent class's initialization. But whether you must call it depends on whether the parent class has important setup work.
Does super() only work with __init__()?
No. You can use it with any inherited method, such as save(), setup(), or run().
What is MRO in Python?
MRO stands for Method Resolution Order. It is the order Python uses to search for methods in an inheritance hierarchy.
Mini Project
Description
Build a small inheritance example for a notification system. This project demonstrates how a subclass can extend parent initialization using super(), while keeping shared setup logic in the base class. It is useful because many real applications have base classes for shared behavior and specialized subclasses for extra features.
Goal
Create a base notification class and a subclass that adds email-specific behavior while reusing the base class initialization with super().
Requirements
- Create a
Notificationclass that stores a message. - Create an
EmailNotificationclass that inherits fromNotification. - Use
super()insideEmailNotification.__init__()to initialize the shared message field. - Add an
emailfield in the subclass. - Print both the message and the email address from an instance of the subclass.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Catch Multiple Exceptions in One except Block in Python
Learn how to catch multiple exceptions in one Python except block using tuples, with examples, mistakes, and real-world usage.
Convert Bytes to String in Python 3
Learn how to convert bytes to str in Python 3 using decode(), text mode, and proper encodings with practical examples.