Capítulo 43, Avançado
MRO, super cooperativo e mixins
A herança múltipla assusta porque quase ninguém aprende a regra que a organiza. Ela é pequena: a linearização C3.
Código deste capítulo: avancado/cap43_mro_mixins.py
A ordem que o Python segue
Cada classe tem uma lista ordenada de superclasses, o MRO. O algoritmo (C3) garante duas coisas: uma classe filha vem sempre antes das suas pais, e a ordem em que você listou as bases é respeitada. No problema do losango, a classe Base aparece uma única vez:
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']
O detalhe que quase todo mundo entende errado: super() não significa "a classe pai". Significa "a próxima classe no MRO do objeto". Dentro de Esquerda, o super().ola() chama a Direita, e não a Base. É isso que faz a cadeia funcionar com cooperação.
__init__ cooperativo
Para que todas as classes de uma hierarquia múltipla sejam inicializadas, cada uma deve consumir os argumentos que lhe pertencem e repassar o resto com 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
Um mixin é uma classe pequena que oferece um comportamento para ser misturado em outras, sem ser uma entidade por si só. Ele não tem estado próprio nem __init__, e o nome costuma terminar em 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}
Ganchos de subclasse
O método __init_subclass__ roda sempre que alguém cria uma subclasse. É a ferramenta mais simples para registrar plugins, e na maioria dos casos substitui uma metaclasse:
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']
Quando o MRO não fecha
Se a ordem das bases contradiz a hierarquia, o Python recusa e diz por quê:
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
Minha regra para herança múltipla
Eu uso herança múltipla em um caso: mixins sem estado, que oferecem um comportamento isolado. Para todo o resto, composição. Quando o
super()cooperativo é necessário em uma hierarquia complexa, isso costuma ser o sinal de que o desenho pediria composição.
Exercício 1
Registro de comandos
Usando __init_subclass__, faça Comando.comandos registrar Listar com o nome "ls" e Remover com o nome "remover", e verifique o resultado.