Python tutorials > Object-Oriented Programming (OOP) > Inheritance > What are single/multiple inheritance?

What are single/multiple inheritance?

This tutorial explains the concepts of single and multiple inheritance in Object-Oriented Programming (OOP) using Python. We will explore their differences, demonstrate their usage with code examples, and discuss their respective advantages and disadvantages. Inheritance is a fundamental concept in OOP that allows you to create new classes (derived classes or subclasses) from existing classes (base classes or superclasses). This promotes code reuse and establishes a hierarchy of classes.

Single Inheritance: Introduction

Definition: Single inheritance is a type of inheritance where a class inherits from only one parent class. This creates a simple and linear class hierarchy.
Example: Imagine a class representing a 'Car'. You could create a 'SportsCar' class that inherits directly from the 'Car' class. The 'SportsCar' class automatically gains all the properties and methods of the 'Car' class and can add its own specific attributes and behaviors.

Single Inheritance: Code Example

Explanation: In this example, the Dog class inherits from the Animal class. The Dog class inherits the __init__ method (which initializes the name attribute) and the speak method from the Animal class. The Dog class also overrides the speak method to provide a specific sound for dogs. The super().__init__(name) call in the Dog's __init__ ensures the parent class's initialization is performed.

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print("Generic animal sound")

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

    def speak(self):
        print("Woof!")

my_dog = Dog("Buddy", "Golden Retriever")
print(my_dog.name)
print(my_dog.breed)
my_dog.speak()

Multiple Inheritance: Introduction

Definition: Multiple inheritance is a type of inheritance where a class inherits from more than one parent class. This allows a class to inherit attributes and methods from multiple sources, combining functionalities from different classes.
Example: Consider creating a class called 'FlyingCar'. This class could inherit from both a 'Car' class and an 'Airplane' class, acquiring properties and behaviors from both.

Multiple Inheritance: Code Example

Explanation: In this example, the Amphibian class inherits from both the Swimmer and Walker classes. As a result, an instance of the Amphibian class can call both the swim() and walk() methods. Python uses the Method Resolution Order (MRO) to determine the order in which methods are inherited when there are conflicts (e.g., if both parent classes have a method with the same name). The MRO can be checked using the __mro__ attribute of the class.

class Swimmer:
    def swim(self):
        print("Swimming...")

class Walker:
    def walk(self):
        print("Walking...")

class Amphibian(Swimmer, Walker):
    pass

frog = Amphibian()
frog.swim()
frog.walk()

Method Resolution Order (MRO)

Explanation: This example demonstrates method resolution order. Class C inherits from both A and B, both of which define a method called method. When we call c.method(), Python uses the MRO to determine which method to call. In this case, it calls A's method first because A appears before B in the class definition of C. The output of C.__mro__ shows the resolution order: (, , , ).

class A:
    def method(self):
        print("A method")

class B:
    def method(self):
        print("B method")

class C(A, B):
    pass

c = C()
c.method()
print(C.__mro__)

Concepts behind the snippet

The core concept behind single and multiple inheritance is code reuse. Inheritance allows you to create new classes based on existing classes, inheriting their attributes and methods, and adding or modifying them as needed. This reduces redundancy and promotes a more organized and maintainable codebase. Key OOP principles like abstraction (hiding complexity) and polymorphism (ability to take on many forms) are also facilitated by inheritance.

Real-Life Use Case Section

GUI Frameworks: In GUI frameworks (like Tkinter or PyQt), widgets often inherit from base widget classes. For example, a 'Button' widget might inherit from a 'Widget' class, inheriting properties like size and position, and adding its own functionality like click handling. Multiple inheritance can be seen where widgets inherit from mixin classes to add functionality like drag-and-drop support.
Game Development: In game development, entities might inherit from base classes like 'GameObject'. Specific entities like 'Player' or 'Enemy' would then inherit from 'GameObject', adding their own unique attributes and behaviors. Multiple inheritance could be used to incorporate traits from different categories, such as an enemy that is both 'Flying' and 'Attacking'.

Best Practices

Favor Composition over Inheritance: In many cases, composition (where a class contains instances of other classes) is a better alternative to multiple inheritance. Composition is often more flexible and avoids the complexities and potential issues associated with multiple inheritance, such as the diamond problem.
Use Mixins Carefully: When using multiple inheritance, consider using mixin classes. Mixins are small, focused classes that provide specific functionalities. They are often used to add features to classes without creating a deep inheritance hierarchy.
Keep Inheritance Hierarchies Simple: Avoid creating overly complex inheritance hierarchies, as they can become difficult to understand and maintain. Aim for a shallow and well-defined hierarchy.

Interview Tip

Be prepared to explain the difference between single and multiple inheritance. Also, be ready to discuss the advantages and disadvantages of each, and when one might be preferred over the other. Understand the concept of the Method Resolution Order (MRO) and how it works. Be prepared to discuss alternatives to multiple inheritance, such as composition.

When to use them

Single Inheritance: Use single inheritance when you want to create a specialized version of an existing class. It's suitable for creating a clear 'is-a' relationship (e.g., a Dog 'is a' Animal).
Multiple Inheritance: Use multiple inheritance when you need to combine functionalities from multiple independent sources. However, use it with caution and consider the potential complexities it introduces. It's best suited for scenarios where mixin classes can provide well-defined, reusable functionalities. If the resulting class is hard to understand then using composition is a better idea.

Memory footprint

The memory footprint of inheritance depends on the number of attributes and methods inherited and added in the derived classes. Multiple inheritance generally has a slightly larger memory footprint than single inheritance due to the overhead of managing multiple parent classes. However, the difference is usually negligible unless you're dealing with a very large number of classes or objects.

Alternatives

Composition: Instead of inheriting from multiple classes, you can create a class that contains instances of other classes. This is often a more flexible and maintainable approach.
Interfaces (Abstract Base Classes): You can define interfaces (abstract base classes) that specify a set of methods that a class must implement. This allows you to enforce a certain contract without inheriting any implementation details.

Pros of Single Inheritance

Simplicity: Easy to understand and implement. Creates a clear and straightforward class hierarchy.
Reduced Complexity: Avoids the ambiguities and complexities associated with multiple inheritance.

Cons of Single Inheritance

Limited Functionality: Can be restrictive if you need to combine functionalities from multiple independent sources.
Potential for Code Duplication: May lead to code duplication if similar functionalities are needed in different parts of the hierarchy.

Pros of Multiple Inheritance

Code Reusability: Allows you to combine functionalities from multiple classes, reducing code duplication.
Increased Flexibility: Can provide a more flexible way to model complex relationships between classes.

Cons of Multiple Inheritance

Complexity: Can be more complex to understand and implement, especially with deep inheritance hierarchies.
The Diamond Problem: A potential issue where a class inherits from two classes that both inherit from a common base class, leading to ambiguity about which version of a method to inherit.
Maintenance Difficulties: Can make the code harder to maintain and debug.

FAQ

  • What is the 'diamond problem' in multiple inheritance?

    The diamond problem occurs when a class inherits from two classes that both inherit from a common base class. This creates a diamond-shaped inheritance hierarchy, and the derived class can inherit multiple copies of the base class's attributes and methods, leading to ambiguity about which version to use. Python resolves this using the Method Resolution Order (MRO), but understanding the MRO is crucial to avoid unexpected behavior.
  • When should I prefer composition over inheritance?

    You should prefer composition over inheritance when you want to achieve code reuse without creating a tight coupling between classes. Composition is more flexible and avoids the complexities and potential issues associated with inheritance, such as the diamond problem. In composition, a class contains instances of other classes as attributes, allowing you to combine functionalities without inheriting their implementations.
  • How does Python resolve method name conflicts in multiple inheritance?

    Python uses the Method Resolution Order (MRO) to determine the order in which methods are inherited in multiple inheritance. The MRO is a deterministic order that ensures that methods are inherited in a predictable and consistent manner. You can view the MRO of a class using the __mro__ attribute.