Inheritance is an important approach in object-oriented programming (OOP), as it allows subclasses to extend the functionality of their parent classes.
Recall the design of the Animal class hierarchy. Suppose we need to implement the following four animals:
If classified by mammals and birds, we can design the class hierarchy as follows:
┌───────────────┐
│ Animal │
└───────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Mammal │ │ Bird │
└─────────────┘ └─────────────┘
│ │
┌─────┴──────┐ ┌─────┴──────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Dog │ │ Bat │ │ Parrot │ │ Ostrich │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
However, if classified by “can run” and “can fly”, the hierarchy should be designed like this:
┌───────────────┐
│ Animal │
└───────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Runnable │ │ Flyable │
└─────────────┘ └─────────────┘
│ │
┌─────┴──────┐ ┌─────┴──────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Dog │ │ Ostrich │ │ Parrot │ │ Bat │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
To include both classification methods above, we would have to design more hierarchical levels:
This makes the class hierarchy overly complex:
┌───────────────┐
│ Animal │
└───────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Mammal │ │ Bird │
└─────────────┘ └─────────────┘
│ │
┌─────┴──────┐ ┌─────┴──────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ MRun │ │ MFly │ │ BRun │ │ BFly │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Dog │ │ Bat │ │ Ostrich │ │ Parrot │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
If we further add “Pet” and “Non-Pet” categories, the number of classes will grow exponentially—this design is clearly unfeasible.
The correct approach is to use multiple inheritance. First, the core class hierarchy is still designed around mammals and birds:
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, to add Runnable and Flyable functionality to the animals, we only need to define the Runnable and Flyable classes first:
class Runnable(object):
def run(self):
print('Running...')
class Flyable(object):
def fly(self):
print('Flying...')
For animals that need the Runnable functionality, we simply inherit from Runnable in addition to their primary parent class (e.g., Dog):
class Dog(Mammal, Runnable):
pass
For animals that need the Flyable functionality, we inherit from Flyable (e.g., Bat):
class Bat(Mammal, Flyable):
pass
Through multiple inheritance, a subclass can inherit all the functionality of multiple parent classes simultaneously.
When designing class inheritance relationships, the main lineage typically follows single inheritance (e.g., Ostrich inherits from Bird). However, if we need to “mix in” additional functionality, we can achieve this through multiple inheritance—for example, making Ostrich inherit from both Bird and Runnable. This design pattern is commonly referred to as MixIn.
To make the inheritance relationship clearer, we rename Runnable and Flyable to RunnableMixIn and FlyableMixIn. Similarly, you could define CarnivorousMixIn (carnivores) and HerbivorousMixIn (herbivores), allowing an animal to have multiple MixIns:
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
The purpose of a MixIn is to add multiple functionalities to a class. Thus, when designing classes, we prioritize combining the functionalities of multiple MixIns through multiple inheritance, rather than creating complex, multi-layered inheritance hierarchies.
Many built-in Python libraries also use MixIns. For example, Python provides TCPServer and UDPServer for network services. To serve multiple users simultaneously, we must use multi-process or multi-thread models—these are provided by ForkingMixIn and ThreadingMixIn. Through combination, we can create the exact service we need:
class MyTCPServer(TCPServer, ForkingMixIn): passclass MyUDPServer(UDPServer, ThreadingMixIn): passIf you want to implement a more advanced coroutine-based model, you could write a CoroutineMixIn:
class MyTCPServer(TCPServer, CoroutineMixIn):
pass
This way, instead of building complex and bloated inheritance chains, we can quickly construct the required subclasses by combining the functionalities of different classes.
Since Python supports multiple inheritance, MixIn is a common and practical design pattern.
Languages that only allow single inheritance (e.g., Java) cannot implement the MixIn design pattern.