6.4 Inheritance in Python

Inheritance is one of the key principles of Object-Oriented Programming (OOP) that allows a new class (called a child class or subclass) to inherit attributes and methods from an existing class (called a parent class or superclass). Inheritance enables code reuse, allows for hierarchical class relationships, and supports polymorphism.

In this section, we’ll explore how to implement inheritance, extend or override methods, and understand the concept of multi-level and multiple inheritance.


6.4.1 What is Inheritance?

Inheritance is a mechanism that allows a class (child class) to inherit attributes and methods from another class (parent class). The child class can use the inherited features as-is, modify them, or add new attributes and methods of its own.

Benefits of Inheritance:

  • Code Reusability: You can reuse existing code in new contexts without rewriting it.
  • Extensibility: You can extend the functionality of an existing class by adding new methods and attributes.
  • Organized Hierarchy: Classes can be organized in a logical hierarchy, making code more modular and easier to maintain.

6.4.2 Defining a Parent Class (Superclass)

A parent class is the class that passes down its properties and behaviors to the child class. You define a parent class like any other class.

Example: Defining a Vehicle Class

class Vehicle:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def start(self):
        print(f"The {self.brand} {self.model} is starting.")

    def stop(self):
        print(f"The {self.brand} {self.model} is stopping.")

Here, the Vehicle class defines two instance attributes (brand and model) and two methods (start() and stop()). This class can now be inherited by other classes to reuse these attributes and methods.


6.4.3 Creating a Child Class (Subclass)

A child class (also called a subclass) inherits the attributes and methods from the parent class. To define a child class, you specify the parent class in parentheses when defining the subclass.

Syntax:

class ChildClass(ParentClass):
    # Additional attributes and methods

Example: Creating a Car Subclass that Inherits from Vehicle

class Car(Vehicle):
    def __init__(self, brand, model, year):
        # Call the parent class's constructor using super()
        super().__init__(brand, model)
        self.year = year  # New attribute specific to Car

    def display_info(self):
        return f"{self.year} {self.brand} {self.model}"

In this example:

  • The Car class inherits from the Vehicle class.
  • The Car class uses the super() function to call the __init__() method of the parent class (Vehicle) to initialize the brand and model attributes.
  • The Car class also adds a new attribute year and defines an additional method display_info().

Creating an Object from the Child Class:

my_car = Car("Toyota", "Corolla", 2020)
my_car.start()  # Output: The Toyota Corolla is starting.
my_car.stop()   # Output: The Toyota Corolla is stopping.
print(my_car.display_info())  # Output: 2020 Toyota Corolla

In this example:

  • The Car object has access to both the inherited methods (start() and stop()) from the Vehicle class and its own method display_info().

6.4.4 The super() Function

The super() function is used to call methods from the parent class. It is commonly used in the child class's __init__() method to ensure that the parent class's constructor is called to properly initialize inherited attributes.

Example:

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    def bark(self):
        print(f"{self.name} says woof!")

class GuardDog(Dog):
    def __init__(self, name, breed, training_level):
        # Call the parent class's constructor
        super().__init__(name, breed)
        self.training_level = training_level

    def display_training(self):
        print(f"{self.name} is trained to level {self.training_level}.")

In this example:

  • The GuardDog class uses super().__init__(name, breed) to call the parent class's constructor, ensuring that the name and breed attributes are properly initialized.

Using the Subclass:

rex = GuardDog("Rex", "German Shepherd", 5)
rex.bark()  # Output: Rex says woof!
rex.display_training()  # Output: Rex is trained to level 5.

Here, the GuardDog object inherits the bark() method from the Dog class and introduces a new method display_training().


6.4.5 Overriding Methods

A child class can override methods from the parent class by defining a method with the same name in the child class. This allows the child class to provide its own implementation while retaining the ability to call the parent class's version using super().

Example: Overriding the start() Method

class ElectricCar(Car):
    def start(self):
        print(f"The {self.brand} {self.model} (Electric) is starting silently.")

In this case:

  • The ElectricCar class overrides the start() method from the Car class, providing a different implementation.

Using the Overridden Method:

my_electric_car = ElectricCar("Tesla", "Model 3", 2021)
my_electric_car.start()  # Output: The Tesla Model 3 (Electric) is starting silently.

The ElectricCar class overrides the start() method to provide a custom behavior, while still inheriting other methods from the parent class.


6.4.6 Extending Methods

You can extend a method from the parent class by calling the parent class's version of the method using super(), and then adding additional functionality in the child class.

Example:

class SmartCar(Car):
    def start(self):
        # Call the parent class's start method
        super().start()
        print(f"The {self.brand} {self.model} is now in smart mode.")

Here, the start() method in SmartCar extends the behavior of the start() method from the parent class by calling super().start() and then adding additional functionality.

Using the Extended Method:

my_smart_car = SmartCar("Tesla", "Model S", 2022)
my_smart_car.start()
# Output:
# The Tesla Model S is starting.
# The Tesla Model S is now in smart mode.

6.4.7 Multi-Level Inheritance

Multi-level inheritance is when a class inherits from another class, which itself inherits from another class. This creates a hierarchy where each class can extend or modify the behavior of its parent.

Example:

class Animal:
    def speak(self):
        print("The animal makes a sound.")

class Dog(Animal):
    def speak(self):
        print("The dog barks.")

class GuardDog(Dog):
    def speak(self):
        print("The guard dog growls.")

# Creating an object of GuardDog
guard_dog = GuardDog()
guard_dog.speak()  # Output: The guard dog growls.

In this example:

  • GuardDog inherits from Dog, which inherits from Animal.
  • The speak() method is overridden at each level, with the most specific version (in GuardDog) being called.

6.4.8 Multiple Inheritance

Multiple inheritance is when a class inherits from more than one parent class. This allows a child class to inherit methods and attributes from multiple sources.

Syntax:

class ChildClass(ParentClass1, ParentClass2):
    # Class definition

Example:

class Flyer:
    def fly(self):
        print("Flying high!")

class Swimmer:
    def swim(self):
        print("Swimming in water!")

class Duck(Flyer, Swimmer):
    pass

# Creating a Duck object
donald = Duck()
donald.fly()   # Output: Flying high!
donald.swim()  # Output: Swimming in water!

In this example:

  • The Duck class inherits from both Flyer and Swimmer, allowing it to use methods from both parent classes.

6.4.9 The isinstance() and issubclass() Functions

  • **

isinstance(): This function checks if an object is an instance of a class or a subclass of that class.

Example:

print(isinstance(my_car, Car))  # Output: True
print(isinstance(my_car, Vehicle))  # Output: True

issubclass()**: This function checks if a class is a subclass of another class.

Example:

print(issubclass(Car, Vehicle))  # Output: True
print(issubclass(ElectricCar, Car))  # Output: True

6.4.10 Summary

  • Inheritance allows a class to inherit attributes and methods from a parent class, enabling code reuse and extensibility.
  • The super() function is used to call methods from the parent class, commonly in constructors or when extending methods.
  • You can override methods in the child class to provide custom behavior and extend methods to add functionality while preserving the parent class’s behavior.
  • Multi-level inheritance creates a hierarchy of inheritance, while multiple inheritance allows a class to inherit from more than one parent class.
  • Use the isinstance() and issubclass() functions to check relationships between objects and classes.

Understanding inheritance is key to building efficient, modular, and scalable applications in Python. It allows you to create hierarchies of classes, extend functionality, and manage code in a more organized way.