I'm referring to "Fluent Python" by Luciano Ramalho. In chapter 12, there's a section "Coping with Multiple Inheritance" where the author suggests a few best practices, such as:
- Distinguish Interface Inheritance from Implementation Inheritance
- Make Interfaces explicit with ABCs (abstract base classes)
- Use Mixins for Code Reuse
If a class is designed to provide method implementations for reuse by multiple unrelated subclasses, without implying an “is-a” relationship, it should be an explicit mixin class. Conceptually, a mixin does not define a new type; it merely bundles methods for reuse. A mixin should never be instantiated, and concrete classes should not inherit only from a mixin. Each mixin should provide a single specific behavior, implementing few and very closely related methods.
Emphasis mine. I get the "shouldn't be instantiated" part; it's not meant to be useful on its own. I don't get the "concrete classes should not inherit only from a mixin". Why not? How would that lead to a bad design?
EDIT: Thanks Christophe for your great answer! Allow me to add one little follow-up request for clarification.
So say I have a mixin that cuts down on the boilerplate needed to define overloaded versions for all the various comparison operators:
class TotalOrderMixin: """In a class with < and == overloaded, this MixIn implements the other comparisons in terms of those.""" def __le__(self, other): return self < other or self == other def __ge__(self, other): return not self < other def __gt__(self, other): return not self <= other ...
Then going by the author's advice, I should not inherit from
TotalOrderMixin in a concrete class. Rather, I should first define
an abstract class:
class MinimumOrder(ABC): @abstractmethod def __lt__(self, other): """self is strictly smaller than other""" @abstractmethod def __eq__(self, other): ""test for equality"""
And then we compose this ABC with the mixin:
class TotalOrderABC(MinimumOrderABC, TotalOrderMixin): pass
and then our concrete class may inherit from that ABC?