Search code examples
pythonmultiple-inheritancemethod-resolution-order

MRO in Python doesn't work as expected


I found an example of multiple inheritance but don't understand the way it behaves.

class Printable:
    """A mixin class for creating a __str__ method that prints
    a sequence object. Assumes that the type difines __getitem__."""

    def lef_bracket(self):
        return type(self).__name__ + "["

    def right_bracket(self):
        return "]"

    def __str__(self):
        result = self.lef_bracket()
        for i in range(len(self)-1):
            result += str(self[i]) + ", "
        if len(self) > 0:
            result += str(self[-1])
        return result + self.right_bracket()

This script is stored in printable.py so the class Printable is used in this way:

>>> from printable import *
>>> class MySeq(list, Printable):
...     pass
... 
>>> my_seq = MySeq([1,2,3])
>>> print(my_seq)
MySeq[1, 2, 3]

My question is that why the __str__ method is inherited from the Printable class instead of the list class, while the Method Resolution Order of MySeq is:

>>> MySeq.__mro__
(<class '__main__.MySeq'>, <class 'list'>, <class 'printable.Printable'>, <class 'object'>)

In the docstring of Printable I notice the word "mixin". Why in this case we call it a mixin class?


Solution

  • list doesn't define a __str__ method:

    >>> '__str__' in list.__dict__
    False
    

    Because it doesn't define such a method, the next class in the MRO gets to supply it. For a plain list object, that'd be object.__str__:

    >>> list.__mro__
    (<class 'list'>, <class 'object'>)
    >>> list.__str__ is object.__dict__['__str__']
    True
    

    but because Printable is mixed in, it is listed before object:

    >>> MySeq.__mro__
    (<class '__main__.MySeq'>, <class 'list'>, <class '__main__.Printable'>, <class 'object'>)
    >>> MySeq.__str__ is Printable.__dict__['__str__']
    True
    

    A mix-in class is a class designed to be added into a class hierarchy to work together with other base classes. Printable is a mix-in because it requires that something else implements __getitem__.