w3resource

Python Inheritance: How to use Inheritance in Classes

Introduction to Python Inheritance

One of the advantages of an Object-Oriented programming language is code reuse. Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class to inherit attributes and methods from another class. This promotes code reusability and logical structure in your programs.

For a deeper understanding of how inheritance works in Python, let's look at some examples.

Example 1: Basic Inheritance

This example illustrates how inheritance allows us to define a common structure and behavior in a base class (Animal), and then customize that behavior in derived classes (Dog and Cat). This approach promotes code reuse and a clean, logical organization of related classes.

Code:

# Base class (Parent class)
class Animal:
    def __init__(self, name):
        self.name = name  # Attribute common to all animals

    def speak(self):
        return f"{self.name} makes a sound."  # Method common to all animals

# Derived class (Child class) inheriting from Animal
class Dog(Animal):
    def speak(self):
        return f"{self.name} barks."  # Overriding the speak method for Dog

# Another Derived class inheriting from Animal
class Cat(Animal):
    def speak(self):
        return f"{self.name} meows."  # Overriding the speak method for Cat

# Creating instances of Dog and Cat
dog = Dog("Rocky")
cat = Cat("Tom")

# Calling the speak method on Dog and Cat instances
print(dog.speak())  # Output: Rocky barks.
print(cat.speak())  # Output: Tom meows.

Explanation:

Base Class (Animal):

  • The Animal class is a base or parent class that represents a generic animal.
  • It has an __init__ method (constructor) that takes a name as an argument and assigns it to an instance variable self.name. This attribute is common to all animals.
  • The speak method returns a generic message indicating that the animal makes a sound. This method is intended to be overridden by derived classes to provide specific behaviors.

Derived Classes (Dog and Cat):

  • Dog Class:
    • The Dog class inherits from the Animal class. This means it automatically gets the attributes and methods defined in Animal.
    • The speak method in the Dog class overrides the speak method in the Animal class. Instead of the generic sound message, it returns a message specific to dogs, indicating that the dog barks.
  • Cat Class:
    • The Cat class also inherits from the Animal class.
    • Similarly, the speak method in the Cat class overrides the speak method from the Animal class to return a message specific to cats, indicating that the cat meows.

Object Creation:

  • We create an instance of the Dog class with the name "Rocky". The Dog class constructor implicitly calls the Animal constructor to initialize the name attribute. When dog.speak() is called, it executes the overridden speak method in the Dog class, resulting in the output: "Rocky barks."
  • We create an instance of the Cat class with the name "Tom". Similar to the Dog class, the Cat class constructor initializes the name attribute by calling the Animal constructor. When cat.speak() is called, it executes the overridden speak method in the Cat class, resulting in the output: "Tom meows."

Example 2: Using the super() Function

This example demonstrates how the super() function facilitates the reuse of methods and constructors from a base class within a derived class. By using super(), the Car class is able to inherit and extend the functionality of the Vehicle class, leading to more organized and maintainable code.

Code:

# Base class
class Vehicle:
    def __init__(self, make, model):
        self.make = make  # Attribute for the manufacturer of the vehicle
        self.model = model  # Attribute for the model of the vehicle

    def info(self):
        return f"Vehicle: {self.make} {self.model}"  # Method to return vehicle information

# Derived class
class Car(Vehicle):
    def __init__(self, make, model, year):
        # Call the constructor of the base class to initialize make and model
        super().__init__(make, model)
        self.year = year  # Additional attribute for the year of manufacture

    def info(self):
        # Call the info method of the base class and extend it to include the year
        return f"{super().info()} ({self.year})"

# Creating an instance of Car
car = Car("Mercedes-Benz", "C 300 Coupe 2D", 2018)

# Calling the info method on the Car instance
print(car.info())  # Output: Vehicle: Mercedes-Benz C 300 Coupe 2D (2018)

Explanation:

Base Class (Vehicle):

  • The Vehicle class serves as the base class, containing attributes that are common to all vehicles:
    • make: The manufacturer of the vehicle.
    • model: The model of the vehicle.
  • The class also defines a method info that returns a string combining the make and model attributes, providing basic information about the vehicle.

Derived Class (Car):

  • The Car class inherits from the Vehicle class and extends its functionality:
    • The Car class introduces an additional attribute year, representing the year the car was manufactured.
    • The __init__ method of the Car class first calls the constructor of the Vehicle class using the super() function to initialize the make and model attributes.
    • The info method in the Car class overrides the info method of the Vehicle class. It calls the base class's info method using super() and then extends the returned string by adding the year information.

super() Function:

  • super() is used to call methods from the base class (Vehicle) within the derived class (Car).
  • In the Car class, super() allows us to:
    • Call the base class constructor: This initializes the make and model attributes in the Vehicle class.
    • Extend the base class method: The info method in Car calls the info method of Vehicle and then adds more details specific to the Car class.

Object Creation and Method Call:

  • An instance of the Car class is created with the make "Mercedes-Benz", model "C 300 Coupe 2D", and year 2018.
  • When car.info() is called, the overridden info method in the Car class is executed:
    • It first calls the info method of the Vehicle class (via super()), which returns the string "Vehicle: Mercedes-Benz C 300 Coupe 2D".
    • The Car class then extends this string by appending the year, resulting in "Vehicle: Mercedes-Benz C 300 Coupe 2D (2018)".

Example 3: Multiple Inheritance

Multiple inheritance is a powerful feature that can enhance code reuse and organization, but it requires careful consideration of MRO (Method Resolution Order) to avoid potential conflicts or unexpected behavior.

This example illustrates the concept of multiple inheritance in Python, where a class can inherit features from more than one base class. The Duck class inherits from both Flyable and Swimmable, allowing it to utilize methods from both classes.

Code:

# Base class 1
class Flyable:
    def fly(self):
        return "This object can fly."

# Base class 2
class Swimmable:
    def swim(self):
        return "This object can swim."

# Derived class inheriting from both Flyable and Swimmable
class Duck(Flyable, Swimmable):
    def quack(self):
        return "Duck quacks."

# Creating an instance of Duck
duck = Duck()

# Calling methods from both base classes and its own method
print(duck.fly())    # Output: This object can fly.
print(duck.swim())   # Output: This object can swim.
print(duck.quack())  # Output: Duck quacks.

Explanation:

Base Class 1 (Flyable):

  • The Flyable class defines a method fly, which returns the string "This object can fly.".
  • This class represents objects that have the capability to fly.

Base Class 2 (Swimmable):

  • The Swimmable class defines a method swim, which returns the string "This object can swim.".
  • This class represents objects that have the capability to swim.

Derived Class (Duck):

  • The Duck class inherits from both Flyable and Swimmable, making it a derived class with access to the methods of both base classes.
  • The Duck class also defines its own method quack, which returns the string "Duck quacks.".

Multiple Inheritance:

  • Definition: Multiple inheritance occurs when a class inherits from more than one base class. In this example, the Duck class inherits from both Flyable and Swimmable.
  • Capabilities of Duck:
    • The Duck class can now use the fly method from the Flyable class, the swim method from the Swimmable class, and its own quack method.
    • This demonstrates how Duck combines functionalities from multiple base classes, embodying the behaviors of both flying and swimming.

Method Resolution Order (MRO):

  • MRO:When a class inherits from multiple base classes, Python uses a specific order to resolve methods and attributes. This is called the Method Resolution Order (MRO).
  • Order of Resolution: The order in which base classes are listed in the derived class definition affects how Python resolves method calls.
    • In this example, if there were methods with the same name in both Flyable and Swimmable, Python would first check the Flyable class (because it’s listed first) and then Swimmable.
  • MRO in Duck: The MRO for Duck ensures that when you call a method on a Duck instance, Python checks the Duck class first, followed by Flyable, and then Swimmable.

Object Creation and Method Calls:

  • An instance of the Duck class is created using duck = Duck().
  • The duck.fly() method call triggers the fly method from the Flyable class, returning "This object can fly.".
  • The duck.swim() method call triggers the swim method from the Swimmable class, returning "This object can swim.".
  • The duck.quack() method call triggers the quack method defined in the Duck class, returning "Duck quacks.".

Example 4: Checking Inheritance

This example illustrates how to check relationships between objects and classes using isinstance() and issubclass() functions in Python.

isinstance(object, classinfo):

Return True if the object argument is an instance of the classinfo argument, or of a (direct, indirect, or virtual) subclass thereof. If object is not an object of the given type, the function always returns False. If classinfo is a tuple of type objects (or recursively, other such tuples) or a Union Type of multiple types, return True if object is an instance of any of the types. If classinfo is not a type or tuple of types and such tuples, a TypeError exception is raised. TypeError may not be raised for an invalid type if an earlier check succeeds.

issubclass(class, classinfo):

Return True if class is a subclass (direct, indirect, or virtual) of classinfo. A class is considered a subclass of itself. classinfo may be a tuple of class objects (or recursively, other such tuples) or a Union Type, in which case return True if class is a subclass of any entry in classinfo. In any other case, a TypeError exception is raised.

Code:

# Base class
class Animal:
    pass

# Derived classes
class Dog(Animal):
    pass

class Cat(Animal):
    pass

# Checking if an object is an instance of a class
dog = Dog()
cat = Cat()

print(isinstance(dog, Dog))      # Output: True
print(isinstance(dog, Animal))   # Output: True
print(isinstance(cat, Cat))      # Output: True
print(isinstance(cat, Dog))      # Output: False

# Checking if a class is a subclass of another
print(issubclass(Dog, Animal))   # Output: True
print(issubclass(Cat, Animal))   # Output: True
print(issubclass(Dog, Cat))      # Output: False

Explanation:

Base Class (Animal):

  • The Animal class is a base class that doesn't define any specific attributes or methods. It simply serves as a parent class for other classes to inherit from.

Derived Classes (Dog and Cat):

  • Dog Class:
    • The Dog class inherits from the Animal class. This means that a Dog is an Animal in the context of inheritance.
  • Cat Class:
    • The Cat class also inherits from the Animal class, meaning that a Cat is also an Animal.

Checking Instances with isinstance():

  • isinstance() is a built-in Python function used to check if an object is an instance of a specified class or a subclass thereof.
  • Checking dog Instance:
    • print(isinstance(dog, Dog)) returns True because dog is indeed an instance of the Dog class.
    • print(isinstance(dog, Animal)) also returns True because Dog is a subclass of Animal, meaning dog is indirectly an instance of Animal.
  • Checking cat Instance:
    • print(isinstance(cat, Cat)) returns True because cat is an instance of the Cat class.
    • print(isinstance(cat, Dog)) returns False because cat is not an instance of the Dog class, and Cat and Dog are independent subclasses of Animal.

Checking Class Relationships with issubclass():

  • issubclass() is a built-in Python function used to check if a class is a subclass of another class.
  • Checking Dog Subclass:
    • print(issubclass(Dog, Animal)) returns True because Dog is directly inherited from Animal.
  • Checking Cat Subclass:
    • print(issubclass(Cat, Animal)) returns True because Cat is also directly inherited from Animal.
  • Checking if Dog is a Subclass of Cat:
    • print(issubclass(Dog, Cat)) returns False because Dog and Cat are siblings, meaning they are both subclasses of Animal but are not subclasses of each other.

Summary

  • Inheritance allows classes to share and extend functionality, promoting code reuse.
  • Method overriding lets derived classes modify or extend the behavior of methods from the base class.
  • The super() function is crucial for accessing methods and attributes of a base class, especially when extending them.
  • Multiple inheritance allows a class to inherit from more than one base class, but care must be taken with the method resolution order (MRO).
  • isinstance() and issubclass() are useful for checking relationships between objects and classes.


Become a Patron!

Follow us on Facebook and Twitter for latest update.