Appearance
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.