Python supports multiple inheritance, while it doesn’t provide the syntax for interface. See PEP-0245. Of course, you can use the abc
module for abstract base classes, which seems tricky.
Python introduces a concept called mixin that encapsulates behavior that can be reused in other classes. It’s supported via multiple inheritance, the difference of inheritance and mixin is, inheritance means “is-a” and mixin means “has-a”.
This post will illustrate how mixin works with a simple example.
Assume we have a class named Animal
and its subclasses Dog
and Chicken
.
class Animal:
pass
class Dog(Animal):
pass
class Chicken(Animal):
pass
These classes make no sense since they have no any method. Let’s add a method representing whether this animal eat meat or not.
class Animal:
pass
class Dog(Animal):
def eatMeat():
return True
class Chicken(Animal):
def eatMeat():
return False
It seems follows the rules of OOD, but how about more subclass, such as Cat
and Goose
.
class Cat(Animal):
def eatMeat():
return True
class Goose(Animal):
def eatMeat():
return False
It appears a code smell. The first idea that comes your mind is improve the inheritance.
class EatMeatAnimal(Animal):
def eatMeat():
return True
class NoMeatAnimal(Animal):
def eatMeat():
return False
class Dog(EatMeatAnimal): pass
class Chicken(NoMeatAnimal): pass
class Cat(EatMeatAnimal): pass
class Goose(NoMeatAnimal): pass
Next, if we want to implement another method hasWings
, we have to append more classes.
class HasWingsEatMeatAnimal(EatMeatAnimal):
def hasWings():
return True
class HasWingsNoMeatAnimal(NoMeatAnimal):
def hasWings():
return True
class NoWingsEatMeatAnimal(EatMeatAnimal):
def hasWings():
return False
class NoWingsNoMeatAnimal(NoMeatAnimal):
def hasWings():
return False
That’s not enough, we must modify our concrete classes too.
class Dog(NoWingsEatMeatAnimal): pass
class Cat(NoWingsEatMeatAnimal): pass
class Chicken(HasWingsNoMeatAnimal): pass
class Goose(HasWingsNoMeatAnimal): pass
I know few people has the patience to read the above dummy code. Let’s see a pythonic way.
class Animal:
pass
class EatMeatMixin:
def eatMeat():
return True
class NoMeatMixin:
def eatMeat():
return False
class HasWingsMixin:
def hasWings():
return True
class NoWingsMixin:
def hasWings():
return False
class Dog(EatMeatMixin, NoWingsMixin, Animal): pass
class Cat(EatMeatMixin, NoWingsMixin, Animal): pass
class Chicken(NoMeatMixin, HasWingsMixin, Animal): pass
class Goose(NoMeatMixin, HasWingsMixin, Animal): pass
That’s mixin attaching behaviors for the main class Animal
.
Actually, mixin is not an advanced feature of Python, but ubiquitous. For example, the built-in socketserver.py
implements a server model based on process or thread with mixin, and this post pave the way for me to analyze the code in the future.
Reference