Inheritance
Learn how one class can inherit attributes and methods from another, override behavior, and reuse code through parent-child class relationships.
Introduction
Suppose you are modeling different kinds of employees: Manager, Developer, and Intern. All of them share common attributes like name and salary, but each also has behavior that is uniquely its own. Rewriting the shared parts in every class would be repetitive and error-prone. Inheritance solves exactly this kind of problem.
In this lesson, you will learn how to create a child class that reuses a parent class's code, how to override methods to specialize behavior, how super() lets a child call back into its parent, and a high-level look at multiple inheritance.
- What inheritance means and the "is-a" relationship it models.
- How to create a child class with class Child(Parent):.
- How to override a parent's method in a child class.
- How to call the parent's method using super().
- How multiple inheritance and method resolution order (MRO) work at a high level.
What is Inheritance?
Inheritance lets one class (the child, or subclass) automatically acquire the attributes and methods of another class (the parent, or superclass), without rewriting that code. The child class can then add new behavior of its own or override inherited behavior.
Inheritance is used to model "is-a" relationships: a Manager is an Employee, a Dog is an Animal, a Circle is a Shape. If the relationship between two things cannot honestly be described with "is-a," inheritance is probably the wrong tool.
Parent Class
The class being inherited from — also called the base or superclass.
Child Class
The class that inherits — also called the subclass or derived class.
Code Reuse
Shared logic is written once in the parent and reused by every child.
Specialization
Child classes can override or extend inherited behavior for their own needs.
Creating a Child Class
To make one class inherit from another, put the parent class name inside parentheses after the child class name: class Child(Parent):. The child automatically gains every attribute and method the parent defines.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} makes a sound.")
class Dog(Animal): # Dog inherits from Animal
pass
d = Dog("Rex")
d.speak() # inherited directly from AnimalRex makes a sound.Dog did not define __init__ or speak() itself — it simply inherited both from Animal. Even an empty child class (using pass) gets everything the parent defines.
Method Overriding
A child class can redefine a method it inherited, replacing the parent's version with its own. This is called method overriding — the method name stays the same, but the behavior changes for that subclass.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} makes a sound.")
class Dog(Animal):
def speak(self): # overrides Animal.speak()
print(f"{self.name} barks.")
class Cat(Animal):
def speak(self): # overrides Animal.speak()
print(f"{self.name} meows.")
Animal("Generic Animal").speak()
Dog("Rex").speak()
Cat("Whiskers").speak()Generic Animal makes a sound.
Rex barks.
Whiskers meows.Calling the Parent with super()
Sometimes you want to override a method but still run the parent's original version as part of the new behavior, instead of replacing it entirely. The super() function gives you a reference to the parent class so you can call its methods from within the child.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} makes a sound.")
class Dog(Animal):
def speak(self):
super().speak() # run the parent's version first
print(f"{self.name} also barks loudly.")
Dog("Rex").speak()Rex makes a sound.
Rex also barks loudly.Extending __init__ in a Subclass
A very common pattern is a subclass that needs extra attributes beyond what the parent stores. Rather than duplicating the parent's __init__, the child calls super().__init__() to handle the shared setup, then adds its own attributes.
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
class Manager(Employee):
def __init__(self, name, salary, team_size):
super().__init__(name, salary) # reuse the parent's setup
self.team_size = team_size # add Manager-specific data
def summary(self):
print(f"{self.name} manages a team of {self.team_size}, earning {self.salary}.")
m = Manager("Aditi", 90000, 6)
m.summary()Aditi manages a team of 6, earning 90000.If a subclass defines its own __init__ without calling super().__init__(), the parent's setup code never runs, and attributes the parent normally sets (like self.name here) will simply not exist on the child's instances.
Multiple Inheritance (Brief Overview)
Python allows a class to inherit from more than one parent class at the same time, unlike languages such as Java. This is called multiple inheritance.
class Flyer:
def fly(self):
print("Flying!")
class Swimmer:
def swim(self):
print("Swimming!")
class Duck(Flyer, Swimmer): # inherits from BOTH parents
pass
d = Duck()
d.fly()
d.swim()Flying!
Swimming!Duck gains behavior from both Flyer and Swimmer. This is powerful, but it can get complicated quickly if multiple parents define methods with the same name — which is where method resolution order comes in.
Method Resolution Order (MRO) — High Level
When a class inherits from multiple parents (or forms a longer chain of inheritance), Python needs a consistent rule to decide which method to use if more than one ancestor defines a method with the same name. This rule is called the Method Resolution Order, and Python computes it automatically using an algorithm called C3 linearization.
class Duck(Flyer, Swimmer):
pass
print(Duck.__mro__)(<class '__main__.Duck'>, <class '__main__.Flyer'>, <class '__main__.Swimmer'>, <class 'object'>)For everyday code, just remember: Python checks the class itself first, then its parents left to right, in the order they were listed. You can always inspect the exact order with ClassName.__mro__ or help(ClassName) if you need certainty.
Why Inheritance Matters
Inheritance is one of the main tools for reducing duplicate code and organizing related classes into a logical hierarchy.
Reuse
Shared behavior lives once, in the parent, instead of being copy-pasted everywhere.
"Is-A" Modeling
Hierarchies like Animal → Dog naturally mirror real-world relationships.
Extensibility
New subclasses can be added without modifying existing, tested parent code.
Common Mistakes
- Forgetting to call super().__init__() when overriding __init__ in a subclass.
- Using inheritance for a "has-a" relationship where composition would fit better (e.g. a Car does not "is-a" Engine — it "has-a" Engine).
- Overriding a method and completely forgetting the parent's version existed, causing confusing bugs.
- Creating deep, tangled inheritance chains that are hard to follow — prefer shallow, clear hierarchies.
Best Practices
- Only use inheritance for genuine "is-a" relationships.
- Call super().__init__() in a subclass constructor whenever the parent needs to run its own setup.
- Keep inheritance hierarchies shallow and easy to reason about.
- Prefer composition ("has-a") over inheritance when the relationship is not a true "is-a".
- Use Class.__mro__ to double-check resolution order when multiple inheritance gets confusing.
Frequently Asked Questions
What is the difference between a parent class and a child class?
A parent class (or superclass) defines shared attributes and methods. A child class (or subclass) inherits from the parent using class Child(Parent): and can reuse, override, or extend that behavior.
Do I always need to call super().__init__()?
You need it whenever your subclass defines its own __init__ but still wants the parent's setup logic to run. If the subclass has no __init__ at all, the parent's __init__ runs automatically.
Is multiple inheritance common in everyday Python code?
It is less common than single inheritance and can add complexity, but it is used deliberately in some patterns, such as mixin classes that add small, reusable pieces of behavior.
What happens if I do not override a parent method?
The child class simply uses the parent's version unchanged — that is the whole point of inheritance, reusing code without rewriting it.
How is inheritance related to polymorphism?
Method overriding, which you learned here, is the main mechanism that enables polymorphism — the same method name behaving differently depending on which class's object calls it. That is covered in depth in the next lesson.
Key Takeaways
- Inheritance lets a child class reuse attributes and methods from a parent class using class Child(Parent):.
- Method overriding lets a child class redefine a parent's method with its own behavior.
- super() lets a child class call the parent's version of a method, often inside an overridden method or __init__.
- Python supports multiple inheritance, where a class inherits from more than one parent.
- Method Resolution Order (MRO) determines which method Python uses when multiple ancestors define the same name.
Summary
Inheritance allows classes to build on top of one another, reusing shared code through a parent-child relationship and specializing behavior through method overriding and super(). It models real "is-a" relationships and, when combined with multiple inheritance, follows a predictable Method Resolution Order.
Next, you will build directly on method overriding to understand polymorphism — how the same method name can behave differently depending on which object calls it.
- You understand what inheritance is and the "is-a" relationship it models.
- You can create child classes and override parent methods.
- You can use super() to call parent methods and extend __init__.
- You understand multiple inheritance and MRO at a high level.
- You are ready to learn about polymorphism.