Skip to content

    Chapter 43, Advanced

    MRO, cooperative super and mixins

    Multiple inheritance is scary because almost no one learns the rule that organizes it. It is small: the C3 linearization.

    Code for this chapter: avancado/cap43_mro_mixins.py

    The order that Python follows

    Each class has an ordered list of superclasses, the MRO. The algorithm (C3) guarantees two things: a child class always comes before its parents, and the order in which you listed the bases is respected. In the diamond problem, the class Base appears only once:

    avancado/cap43_mro_mixins.pylinhas 10 a 31
    class Base:
        def ola(self):
            return ["Base"]
    
    
    class Esquerda(Base):
        def ola(self):
            return ["Esquerda"] + super().ola()
    
    
    class Direita(Base):
        def ola(self):
            return ["Direita"] + super().ola()
    
    
    class Topo(Esquerda, Direita):
        def ola(self):
            return ["Topo"] + super().ola()
    
    
    print(Topo().ola())
    print([c.__name__ for c in Topo.__mro__])
    
    Output
    ['Topo', 'Esquerda', 'Direita', 'Base']
    ['Topo', 'Esquerda', 'Direita', 'Base', 'object']
    

    The detail that almost everyone misunderstands: super() does not means "the parent class". It means "the next class in the object's MRO". Inside Esquerda, the super().ola() calls the Direita, and not the Base. That is what makes the chain work with cooperation.

    Cooperative __init__

    For all classes in a multiple hierarchy to be initialized, each must consume the arguments that belong to it and pass on the rest with super().__init__(**kwargs):

    avancado/cap43_mro_mixins.pylinhas 36 a 53
    class Nomeavel:
        def __init__(self, *, nome, **kwargs):
            super().__init__(**kwargs)
            self.nome = nome
    
    
    class Datavel:
        def __init__(self, *, data, **kwargs):
            super().__init__(**kwargs)
            self.data = data
    
    
    class Evento(Nomeavel, Datavel):
        pass
    
    
    e = Evento(nome="reunião", data="2026-10-06")
    print(e.nome, e.data)
    
    Output
    reunião 2026-10-06
    

    Mixins

    An mixin is a small class that offers a behavior to be mixed into others, without being an entity by itself. It has no own state nor __init__, and the name usually ends with Mixin:

    avancado/cap43_mro_mixins.pylinhas 58 a 80
    import json
    
    
    class JsonMixin:
        def para_json(self):
            return json.dumps(vars(self), ensure_ascii=False)
    
    
    class ReprMixin:
        def __repr__(self):
            campos = ", ".join(f"{k}={v!r}" for k, v in vars(self).items())
            return f"{type(self).__name__}({campos})"
    
    
    class Usuario(JsonMixin, ReprMixin):
        def __init__(self, nome, idade):
            self.nome = nome
            self.idade = idade
    
    
    u = Usuario("Ana", 30)
    print(u)
    print(u.para_json())
    
    Output
    Usuario(nome='Ana', idade=30)
    {"nome": "Ana", "idade": 30}
    

    Subclass hooks

    The method __init_subclass__ runs whenever someone creates a subclass. It is the simplest tool for registering plugins, and in most cases replaces a metaclass:

    avancado/cap43_mro_mixins.pylinhas 85 a 101
    class Comando:
        comandos = {}
    
        def __init_subclass__(cls, nome=None, **kwargs):
            super().__init_subclass__(**kwargs)
            Comando.comandos[nome or cls.__name__.lower()] = cls
    
    
    class Listar(Comando, nome="ls"):
        pass
    
    
    class Remover(Comando):
        pass
    
    
    print(sorted(Comando.comandos))
    
    Output
    ['ls', 'remover']
    

    When the MRO does not close

    If the order of the bases contradicts the hierarchy, Python refuses and explains why:

    avancado/cap43_mro_mixins.pylinhas 106 a 118
    class X:
        pass
    
    
    class Y(X):
        pass
    
    
    try:
        class Z(X, Y):
            pass
    except TypeError as erro:
        print(erro)
    
    Output
    Cannot create a consistent method resolution
    order (MRO) for bases X, Y
    

    My rule for multiple inheritance

    I use multiple inheritance in one case: stateless mixins, which offer isolated behavior. For everything else, composition. When the super() cooperative is necessary in a complex hierarchy, it is often a sign that the design would call for composition.

    Exercise 1

    Command registration

    Using __init_subclass__, make Comando.comandos register Listar with the name "ls" and Remover with the name "remover", and check the result.