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:
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__])
['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):
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)
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:
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())
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:
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))
['ls', 'remover']
When the MRO does not close
If the order of the bases contradicts the hierarchy, Python refuses and explains why:
class X:
pass
class Y(X):
pass
try:
class Z(X, Y):
pass
except TypeError as erro:
print(erro)
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.