Skip to content
On this page

Multiple Inheritance

Inheritance is an important aspect of object-oriented programming because it allows subclasses to extend the functionality of their parent classes.

Recall the design of the Animal class hierarchy. Suppose we want to implement the following four animals:

  • Dog
  • Bat
  • Parrot
  • Ostrich

If we categorize them as mammals and birds, we can design the class hierarchy as follows:

                ┌───────────────┐
                │    Animal     │
                └───────────────┘

           ┌────────────┴────────────┐
           │                         │
           ▼                         ▼
    ┌─────────────┐           ┌─────────────┐
    │   Mammal    │           │    Bird     │
    └─────────────┘           └─────────────┘
           │                         │
     ┌─────┴──────┐            ┌─────┴──────┐
     │            │            │            │
     ▼            ▼            ▼            ▼
┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
│   Dog   │  │   Bat   │  │ Parrot  │  │ Ostrich │
└─────────┘  └─────────┘  └─────────┘  └─────────┘

However, if we categorize them based on "can run" and "can fly," the class hierarchy should be designed as follows:

                ┌───────────────┐
                │    Animal     │
                └───────────────┘

           ┌────────────┴────────────┐
           │                         │
           ▼                         ▼
    ┌─────────────┐           ┌─────────────┐
    │  Runnable   │           │   Flyable   │
    └─────────────┘           └─────────────┘
           │                         │
     ┌─────┴──────┐            ┌─────┴──────┐
     │            │            │            │
     ▼            ▼            ▼            ▼
┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
│   Dog   │  │ Ostrich │  │ Parrot  │  │   Bat   │
└─────────┘  └─────────┘  └─────────┘  └─────────┘

If we want to include both classifications above, we need to design more levels:

  • Mammals: Running mammals, flying mammals
  • Birds: Running birds, flying birds

This makes the class hierarchy more complex:

                ┌───────────────┐
                │    Animal     │
                └───────────────┘

           ┌────────────┴────────────┐
           │                         │
           ▼                         ▼
    ┌─────────────┐           ┌─────────────┐
    │   Mammal    │           │    Bird     │
    └─────────────┘           └─────────────┘
           │                         │
     ┌─────┴──────┐            ┌─────┴──────┐
     │            │            │            │
     ▼            ▼            ▼            ▼
┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
│  MRun   │  │  MFly   │  │  BRun   │  │  BFly   │
└─────────┘  └─────────┘  └─────────┘  └─────────┘
     │            │            │            │
     │            │            │            │
     ▼            ▼            ▼            ▼
┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐
│   Dog   │  │   Bat   │  │ Ostrich │  │ Parrot  │
└─────────┘  └─────────┘  └─────────┘  └─────────┘

If we continue to add categories like "Pet" and "Non-pet," the number of classes will grow exponentially, making this design impractical.

The Correct Approach: Multiple Inheritance

The correct approach is to use multiple inheritance. First, the main class hierarchy remains based on mammals and birds:

python
class Animal(object):
    pass

# Major categories:
class Mammal(Animal):
    pass

class Bird(Animal):
    pass

# Various animals:
class Dog(Mammal):
    pass

class Bat(Mammal):
    pass

class Parrot(Bird):
    pass

class Ostrich(Bird):
    pass

Now, if we want to add Runnable and Flyable functionalities to animals, we simply define the Runnable and Flyable classes:

python
class Runnable(object):
    def run(self):
        print('Running...')

class Flyable(object):
    def fly(self):
        print('Flying...')

For animals that require the Runnable functionality, we use multiple inheritance. For example, for Dog:

python
class Dog(Mammal, Runnable):
    pass

For animals that require the Flyable functionality, such as Bat:

python
class Bat(Mammal, Flyable):
    pass

Through multiple inheritance, a subclass can inherit functionalities from multiple parent classes.

MixIn

When designing class inheritance relationships, the main lineage typically follows single inheritance. For example, Ostrich inherits from Bird. However, if we need to "mix in" additional functionalities, multiple inheritance allows us to do so. For instance, to make Ostrich inherit from both Bird and Runnable, we can design it as follows. This design pattern is often referred to as a MixIn.

To better illustrate the inheritance relationships, let's rename Runnable and Flyable to RunnableMixIn and FlyableMixIn. Similarly, you can define CarnivorousMixIn for carnivorous animals and HerbivoresMixIn for herbivorous animals, allowing an animal to possess multiple MixIns:

python
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
    pass

The purpose of a MixIn is to add multiple functionalities to a class. When designing classes, we prioritize combining multiple MixIns through multiple inheritance rather than creating a complex multi-level inheritance hierarchy.

Many of Python's built-in libraries also use MixIns. For example, Python provides TCPServer and UDPServer for network services. To support multiple users simultaneously, you must use multi-process or multi-threaded models, which are provided by ForkingMixIn and ThreadingMixIn, respectively. By combining these, you can create appropriate services.

For example, to write a multi-process TCP server, define it as follows:

python
class MyTCPServer(TCPServer, ForkingMixIn):
    pass

To write a multi-threaded UDP server, define it as follows:

python
class MyUDPServer(UDPServer, ThreadingMixIn):
    pass

If you plan to implement a more advanced coroutine model, you can create a CoroutineMixIn:

python
class MyTCPServer(TCPServer, CoroutineMixIn):
    pass

In this way, we avoid complex and large inheritance chains. By choosing to combine different class functionalities, we can quickly construct the desired subclasses.

Summary

Because Python allows multiple inheritance, MixIns are a common design pattern.

Languages that only allow single inheritance (such as Java) cannot utilize MixIn designs.

Multiple Inheritance has loaded