Herança múltipla e MRO
1. Introdução à herança múltipla em Python
Herança múltipla é um recurso poderoso da programação orientada a objetos que permite que uma classe filha herde atributos e métodos de duas ou mais classes pai. Em Python, essa funcionalidade é implementada de forma nativa e elegante, diferentemente de linguagens como Java que não a suportam diretamente.
A sintaxe básica é simples: basta listar as classes pai separadas por vírgula na definição da classe filha.
class Mamifero:
def som(self):
return "Som de mamífero"
class Voador:
def som(self):
return "Som de voador"
class Morcego(Mamifero, Voador):
def __init__(self, nome):
self.nome = nome
batman = Morcego("Batman")
print(batman.som()) # Qual método será chamado?
Neste exemplo, Morcego herda de Mamifero e Voador, ambas com um método som(). A pergunta crucial é: qual delas será executada? A resposta está no MRO.
2. O problema do diamante
O "problema do diamante" é um desafio clássico da herança múltipla. Ele ocorre quando uma classe herda de duas classes que, por sua vez, herdam de uma mesma classe ancestral.
class A:
def metodo(self):
print("Método de A")
class B(A):
def metodo(self):
print("Método de B")
class C(A):
def metodo(self):
print("Método de C")
class D(B, C):
pass
d = D()
d.metodo() # Qual método será chamado?
Em linguagens como C++, isso pode gerar ambiguidades que exigem soluções complexas como herança virtual. Python, no entanto, resolve esse problema de forma transparente através do MRO, utilizando o algoritmo C3 Linearization.
3. MRO (Method Resolution Order) – ordem de resolução de métodos
O MRO é o mecanismo que determina a ordem em que as classes são pesquisadas quando um método é chamado em um objeto. Python utiliza o algoritmo C3 Linearization, que garante três propriedades:
- Subclasses vêm antes das superclasses
- A ordem de declaração das classes pai é preservada
- A hierarquia é consistente e monotônica
Para inspecionar o MRO de uma classe, podemos usar o atributo __mro__ ou o método mro():
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
A ordem segue da classe atual até object, passando pelas classes pai na ordem declarada, respeitando a hierarquia.
4. Visualizando e entendendo o MRO na prática
Vamos criar um exemplo mais complexo para entender como o MRO funciona:
class Animal:
def __init__(self):
print("Animal init")
class Mamifero(Animal):
def __init__(self):
print("Mamifero init")
super().__init__()
class Ave(Animal):
def __init__(self):
print("Ave init")
super().__init__()
class Ornitorrinco(Mamifero, Ave):
def __init__(self):
print("Ornitorrinco init")
super().__init__()
print(Ornitorrinco.__mro__)
# (<class '__main__.Ornitorrinco'>, <class '__main__.Mamifero'>,
# <class '__main__.Ave'>, <class '__main__.Animal'>, <class 'object'>)
perry = Ornitorrinco()
# Ornitorrinco init
# Mamifero init
# Ave init
# Animal init
Observe como super().__init__() segue o MRO, chamando os métodos __init__ na ordem correta: Ornitorrinco → Mamifero → Ave → Animal → object.
5. Trabalhando com super() em herança múltipla
O super() em Python não chama simplesmente a classe pai imediata, mas sim o próximo na cadeia do MRO. Isso permite uma cooperação coordenada entre as classes na hierarquia.
class Base:
def __init__(self, **kwargs):
print(f"Base init: {kwargs}")
self.base_attr = kwargs.get('base_attr')
class MixinA:
def __init__(self, **kwargs):
print(f"MixinA init: {kwargs}")
self.a_attr = kwargs.get('a_attr')
super().__init__(**kwargs)
class MixinB:
def __init__(self, **kwargs):
print(f"MixinB init: {kwargs}")
self.b_attr = kwargs.get('b_attr')
super().__init__(**kwargs)
class Concreta(MixinA, MixinB, Base):
def __init__(self, **kwargs):
print(f"Concreta init: {kwargs}")
super().__init__(**kwargs)
obj = Concreta(base_attr=1, a_attr=2, b_attr=3)
print(Concreta.__mro__)
Cuidado: Ao usar super() em cadeia, as assinaturas dos métodos devem ser compatíveis. O uso de **kwargs é uma técnica comum para contornar esse problema.
6. Boas práticas e armadilhas comuns
Armadilhas:
- Inicialização inconsistente: Se uma classe na hierarquia não chamar
super().__init__(), a cadeia é quebrada - Assinaturas incompatíveis: Métodos com parâmetros diferentes podem causar erros
- Complexidade excessiva: Herança múltipla profunda torna o código difícil de entender
Boas práticas:
- Prefira composição sobre herança múltipla sempre que possível
- Use herança múltipla principalmente para mixins
- Mantenha a hierarquia rasa (no máximo 2-3 níveis)
- Documente o MRO esperado para classes complexas
7. Mixins: aplicação prática da herança múltipla
Mixins são classes pequenas e focadas que adicionam funcionalidades específicas. Eles são uma das aplicações mais elegantes da herança múltipla em Python.
class JSONMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class LogMixin:
def log(self, mensagem):
print(f"[LOG] {self.__class__.__name__}: {mensagem}")
class SerializavelMixin:
def serialize(self):
return str(self.__dict__)
class Usuario(JSONMixin, LogMixin, SerializavelMixin):
def __init__(self, nome, email):
self.nome = nome
self.email = email
self.log("Usuário criado")
class Produto(JSONMixin, LogMixin):
def __init__(self, nome, preco):
self.nome = nome
self.preco = preco
self.log("Produto criado")
user = Usuario("João", "joao@email.com")
print(user.to_json())
print(user.serialize())
prod = Produto("Notebook", 3500.00)
print(prod.to_json())
Mixins resolvem o problema do diamante de forma limpa porque são classes pequenas e independentes, sem hierarquia complexa entre si.
8. Conclusão e comparação com outros temas da série
O MRO é a solução elegante do Python para o desafio da herança múltipla. Diferentemente de linguagens como C++, que exigem herança virtual para resolver o problema do diamante, Python oferece uma abordagem transparente e previsível através do algoritmo C3 Linearization.
Enquanto a herança simples (tema vizinho desta série) estabelece uma hierarquia linear clara, a herança múltipla permite combinar comportamentos de forma flexível. O MRO trabalha em conjunto com o polimorfismo, resolvendo dinamicamente qual método executar com base na ordem linearizada.
Recomendação final: Use herança múltipla com moderação. Sempre verifique o MRO com Classe.__mro__ ao trabalhar com hierarquias complexas. Prefira mixins para adicionar funcionalidades transversais e considere composição como alternativa mais simples e testável.
Referências
- Python Documentation: Multiple Inheritance — Documentação oficial do Python sobre herança múltipla, com exemplos básicos e explicação do MRO
- Python MRO: Method Resolution Order — Artigo técnico oficial da Python Software Foundation explicando o algoritmo C3 Linearization em detalhes
- Real Python: Multiple Inheritance in Python — Tutorial abrangente com exemplos práticos de herança múltipla, MRO e boas práticas
- GeeksforGeeks: Method Resolution Order (MRO) in Python — Guia detalhado sobre MRO com exemplos visuais e explicação do algoritmo C3
- Stack Abuse: Python Multiple Inheritance and MRO — Artigo técnico abordando problemas comuns e soluções para herança múltipla em Python
- Python Docs: super() function — Documentação oficial da função
super()e seu comportamento em hierarquias com herança múltipla