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:

  1. Distinguish Interface Inheritance from Implementation Inheritance
  2. Make Interfaces explicit with ABCs (abstract base classes)
  3. Use Mixins for Code Reuse

I'll quote:

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?

有帮助吗?

解决方案

Mixins are a way to compose functionality at the level of classes instead of using object composition. The idea is to have a base class and enrich it by combining it with reusable mixins using multiple inheritance.

Since mixins do not really define a type, you should not instantiate them. You already got this.

The usual way is therefore to have an independent base class and create derived classes that inherits the base class and one or several mixins.

You could of course imagine a concrete base class (i.e. meant to be instantiated) for your own type that already inherits from a mixin. But this is not advised because:

  • Your base class is coupled to a mixin. This breaks the idea of having the mixin used by multiple unrelated classes.
  • A class inheriting only from a single mixin could be understood as a specialisation of that mixin (OO semantic of inheritance). The expectation would hence be that it is not instantiated directly. This kind of construct is therefore a source of ambiguity.

This is why the author suggests this “best practice”. (Unrelated to your question: personally, despite having heavily used multiple inheritance, I still prefer composition over inheritance and find the mixin gymnastics quite challenging, if not counterintuitive in view of OO inheritance semantics)

许可以下: CC-BY-SA归因
scroll top