Chapter 4: Inheritance
What is Inheritance?
Inheritance is a fundamental concept in Object-Oriented Programming that allows a new class to be based on an existing class. The new class inherits attributes and methods from the existing class, enabling code reuse and the creation of hierarchical relationships between classes.
Key concepts in inheritance:
- Base class (or Parent class): The existing class that is being inherited from.
- Derived class (or Child class): The new class that inherits from the base class.
- Superclass: Another term for the base class.
- Subclass: Another term for the derived class.
Why Use Inheritance?
- Code Reusability: Inherit common attributes and methods from a base class, reducing redundant code.
- Extensibility: Easily add new features to existing classes without modifying them.
- Hierarchical Classification: Represent relationships between classes in a logical, hierarchical structure.
- Polymorphism: Enable polymorphic behavior (which we'll cover in the next chapter).
Method Overriding
Method overriding occurs when a derived class provides a specific implementation for a method that is already defined in its base class. This allows the derived class to change or extend the behavior of inherited methods.
Real-life Example: An Animal Hierarchy
Let's create an Animal hierarchy to demonstrate inheritance. We'll have a base Animal class and derived classes for Dog, Cat, and Bird.
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
pass # This will be overridden by subclasses
def describe(self):
return f"{self.name} is {self.age} years old"
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age)
self.breed = breed
def speak(self):
return "Woof!"
def describe(self):
return f"{super().describe()} and is a {self.breed}"
class Cat(Animal):
def __init__(self, name, age, color):
super().__init__(name, age)
self.color = color
def speak(self):
return "Meow!"
def describe(self):
return f"{super().describe()} and has {self.color} fur"
class Bird(Animal):
def __init__(self, name, age, species):
super().__init__(name, age)
self.species = species
def speak(self):
return "Chirp!"
def describe(self):
return f"{super().describe()} and is a {self.species}"
def fly(self):
return f"{self.name} is flying high!"
# Using the classes
dog = Dog("Buddy", 5, "Golden Retriever")
cat = Cat("Whiskers", 3, "tabby")
bird = Bird("Tweety", 2, "Canary")
animals = [dog, cat, bird]
for animal in animals:
print(animal.describe())
print(f"{animal.name} says: {animal.speak()}")
if isinstance(animal, Bird):
print(animal.fly())
print() # Empty line for readability
# Output:
# Buddy is 5 years old and is a Golden Retriever
# Buddy says: Woof!
#
# Whiskers is 3 years old and has tabby fur
# Whiskers says: Meow!
#
# Tweety is 2 years old and is a Canary
# Tweety says: Chirp!
# Tweety is flying high!
This example demonstrates several key aspects of inheritance:
- Base Class: The
Animalclass serves as the base class, defining common attributes (nameandage) and methods (speakanddescribe) for all animals. - Derived Classes:
Dog,Cat, andBirdare derived classes that inherit fromAnimal. They all have access to the attributes and methods of theAnimalclass. - Extending Functionality: Each derived class adds its own unique attribute (
breedfor Dog,colorfor Cat,speciesfor Bird). - Method Overriding: All derived classes override the
speakmethod to provide their own specific implementation. They also override thedescribemethod to include their unique attributes. - Super() Function: The
super()function is used in the derived classes to call methods from the base class. This allows us to extend the functionality of the base class methods rather than completely replacing them. - Unique Methods: The
Birdclass has a uniqueflymethod, demonstrating that derived classes can have methods not present in the base class. - Polymorphism: In the loop where we iterate through the
animalslist, we can calldescribeandspeakon each animal regardless of its specific type. This is an example of polymorphism, which we'll explore more in the next chapter.
Benefits of This Design
- Code Reuse: Common attributes and methods are defined once in the
Animalclass and reused in all derived classes. - Extensibility: We can easily add new animal types (e.g.,
Fish) without modifying existing code. - Flexibility: Each animal type can have its own unique behaviors while still sharing common traits.
- Maintainability: If we need to change something common to all animals, we only need to modify the
Animalclass.
Considerations When Using Inheritance
- Single Inheritance vs. Multiple Inheritance: Some languages (like Python) support multiple inheritance, where a class can inherit from multiple base classes. This can be powerful but also introduces complexity.
- Depth of Inheritance: Be cautious about creating too deep of an inheritance hierarchy, as it can make the code harder to understand and maintain.
- Liskov Substitution Principle: Derived classes should be substitutable for their base classes without affecting the correctness of the program.
- Favor Composition Over Inheritance: In some cases, it might be better to use composition (has-a relationship) instead of inheritance (is-a relationship).
Inheritance is a powerful tool in OOP, allowing for the creation of well-structured, reusable, and extensible code. However, it should be used judiciously and in conjunction with other OOP principles to create the most effective and maintainable software designs.