Inheritance: Creating Derived Classes and Using `super()`
We have mastered the art of creating self-contained classes that bundle data and behavior. But what if we have multiple classes that share some common logic? For example, a Dog, a Cat, and a Fish are all types of Animal. They might all have a name and an age, but each makes a different sound.
Instead of copying and pasting the common code into each class, we can use inheritance. Inheritance is a fundamental pillar of OOP that allows a new class (the "child" or "derived" class) to absorb the attributes and methods of an existing class (the "parent" or "base" class).
📚 Prerequisites
You should be comfortable creating a basic class with an __init__ method, attributes, and methods.
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ The Concept of Inheritance: Understand the "is-a" relationship between classes.
- ✅ Creating a Subclass: The syntax for making one class inherit from another.
- ✅ The
super()Function: How to call methods on the parent class from within the child class, especially for__init__(). - ✅ Method Overriding: How a child class can provide its own specific implementation of a parent's method.
🧠 Section 1: The "Is-A" Relationship
Inheritance models an "is-a" relationship.
- A
Dogis anAnimal. - A
Caris aVehicle. - A
SavingsAccountis a type ofBankAccount.
This relationship means the child class gets all the functionality of the parent class for free, and can then add its own unique features or modify existing ones.
The Syntax: To make a class inherit from another, you place the parent class's name in parentheses after the child class's name.
class Parent:
pass
class Child(Parent): # Child inherits from Parent
pass
💻 Section 2: A Practical Example - Animal and Dog
Let's create a generic Animal base class.
# inheritance_example.py
class Animal:
def __init__(self, name: str, age: int):
print("Animal __init__ called")
self.name = name
self.age = age
def speak(self):
# This is a generic implementation
return "Some generic animal sound"
def display(self):
print(f"I am an animal named {self.name}, and I am {self.age} years old.")
Now, let's create a Dog class that inherits from Animal. A dog is an animal, but it also has a breed and makes a specific sound.
class Dog(Animal):
def __init__(self, name: str, age: int, breed: str):
print("Dog __init__ called")
# How do we initialize the name and age from the parent?
# We use super()!
super().__init__(name, age)
self.breed = breed # This is a Dog-specific attribute
# This is method overriding
def speak(self):
return "Woof!"
🛠️ Section 3: The super() Function
In the Dog class's __init__ method, we need to initialize the name and age attributes defined in the Animal class. Instead of rewriting self.name = name and self.age = age, we can call the parent's __init__ method directly.
The super() function gives you a proxy object that allows you to access methods of the parent class. super().__init__(name, age) means "call the __init__ method of my parent class and pass it name and age."
This is crucial because it keeps our code DRY (Don't Repeat Yourself). If we ever add a new attribute to the Animal's __init__, we only have to change it in one place, and all child classes that use super() will automatically inherit the change.
🚀 Section 4: Method Overriding
Notice that both Animal and Dog have a speak() method. When a child class defines a method that has the same name as a method in its parent class, it's called method overriding.
When you call speak() on a Dog object, Python is smart enough to use the Dog's version of the method, not the Animal's.
Let's see it all in action:
# --- Using our classes ---
# Create an instance of the child class
my_dog = Dog("Buddy", 4, "Golden Retriever")
# The 'display' method was inherited directly from Animal
my_dog.display()
# The 'speak' method was overridden by the Dog class
sound = my_dog.speak()
print(f"{my_dog.name} says: {sound}")
# The 'breed' attribute is specific to the Dog class
print(f"{my_dog.name} is a {my_dog.breed}.")
Output:
Dog __init__ called
Animal __init__ called
I am an animal named Buddy, and I am 4 years old.
Buddy says: Woof!
Buddy is a Golden Retriever.
This example perfectly illustrates the power of inheritance. Our Dog class got the display() method for free from Animal, provided its own special version of speak(), and added its own unique data (breed).
✨ Conclusion & Key Takeaways
Inheritance is a cornerstone of OOP that allows you to create a logical hierarchy of classes, reducing code duplication and making your programs more organized and extensible.
Let's summarize the key takeaways:
- Inheritance models an "is-a" relationship, allowing a child class to get attributes and methods from a parent class.
- Create a subclass with
class Child(Parent):. - Use
super()to call methods on the parent class, which is essential for properly initializing parent attributes from the child's__init__. - Method Overriding allows a child class to provide a more specific implementation of a parent's method.
➡️ Next Steps
Inheritance is just one of the major pillars of advanced OOP. The next logical concept is Polymorphism, which allows us to treat objects of different classes in the same way. We'll explore this in the next article.
Happy coding!