Módulo 10 - Orientação a objetos avançada

Herança múltipla e a ordem de resolução

10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 02/07/2026

O que você vai aprender

  • Entender herança múltipla e o problema do diamante.
  • Ler a MRO de uma classe com __mro__.
  • Usar super() como o próximo da cadeia, não só o pai.
  • Reconhecer mixins como uso saudável de herança múltipla.

Herdar de várias classes

Diferente de várias linguagens, o Python permite herança múltipla: uma classe pode ter mais de uma base. Você escreve class Anfibio(Terrestre, Aquatico) e a nova classe herda de ambas. Isso é poderoso, mas abre uma pergunta incômoda. Se tanto Terrestre quanto Aquatico definem um método mover, qual deles a classe Anfibio usa? E se as duas, por sua vez, herdam de uma base comum, digamos Animal, quantas vezes o construtor de Animal roda? Esse emaranhado é o famoso problema do diamante, pelo formato do diagrama de herança. O Python resolve tudo isso com uma regra clara e determinística, a ordem de resolução de métodos.

A MRO é a lista ordenada das classes em que o Python procura um método, da mais específica para a mais geral. Quando você chama um método em um objeto, o Python percorre essa lista e usa a primeira classe que o define. Você pode ver a MRO de qualquer classe: ela está no atributo __mro__ ou no método mro(). Ler essa lista é a forma definitiva de entender por que um método específico foi escolhido, sem adivinhação. A ordem não é aleatória; ela segue um algoritmo que respeita a ordem em que você declarou as bases e garante que cada classe apareça uma única vez.

Lendo a MRO

Ver a MRO na prática dissolve o mistério. No exemplo, D herda de B e C, que herdam de A. A MRO resultante é D, depois B, depois C, depois A e por fim object, a base de tudo. Quando você chama um método em um objeto D, o Python procura nessa ordem e para na primeira classe que o tem. Repare que A, a base comum, aparece uma única vez, no fim, mesmo sendo ancestral de B e de C. É essa garantia que resolve o problema do diamante: nenhuma classe é visitada duas vezes, então a base comum roda uma vez só, não uma por caminho.

class A:
    def quem(self):
        return "A"

class B(A):
    def quem(self):
        return "B"

class C(A):
    def quem(self):
        return "C"

class D(B, C):
    pass

print([cls.__name__ for cls in D.__mro__])
# ['D', 'B', 'C', 'A', 'object']
print(D().quem())   # B  (primeira da MRO que define quem)

A MRO de D é D, B, C, A, object. A base comum A aparece uma única vez, no fim.

🎮 Jogo da aula

Monte a MRO do diamante

Considere o diamante: class A: ..., class B(A): ..., class C(A): ... e class D(B, C): .... Toque as classes na ordem em que o Python as visita ao procurar um método num objeto D (a MRO), da primeira à última.

    super() é o próximo da cadeia

    Aqui está a peça que confunde quase todo mundo. Muita gente aprende super() como uma forma de chamar a classe pai, e em herança simples é isso mesmo. Mas com herança múltipla a definição correta é outra: super() chama o próximo método na MRO, que nem sempre é o pai declarado. Isso é o que torna a herança cooperativa possível. Quando cada classe da hierarquia chama super() no seu método, o Python vai encadeando as chamadas ao longo da MRO, e cada classe contribui com a sua parte uma única vez. É assim que um construtor consegue inicializar todas as bases sem repetir a base comum, respeitando o diamante.

    class Base:
        def __init__(self):
            print("Base")
    
    class Esquerda(Base):
        def __init__(self):
            print("Esquerda")
            super().__init__()   # proximo da MRO, nao "o pai"
    
    class Direita(Base):
        def __init__(self):
            print("Direita")
            super().__init__()
    
    class Junta(Esquerda, Direita):
        def __init__(self):
            print("Junta")
            super().__init__()
    
    Junta()
    # Junta / Esquerda / Direita / Base
    # Base roda UMA vez, gracas a MRO e ao super() cooperativo

    Cada __init__ chama super(); a cadeia segue a MRO e Base roda uma única vez.

    O uso mais saudável e comum de herança múltipla são os mixins. Um mixin é uma classe pequena, feita para ser misturada a outras, que acrescenta um comportamento bem definido sem representar uma entidade completa. Uma classe SerializavelJsonMixin que dá o método para virar JSON, uma LogavelMixin que acrescenta registro de eventos. Você combina esses comportamentos com a classe principal por herança múltipla, e cada mixin contribui com sua fatia. Quando a herança múltipla vira mixins de comportamento, e não uma tentativa de modelar uma coisa que é várias coisas ao mesmo tempo, ela fica clara e fácil de manter.

    Teste rápido

    Em herança múltipla, o que super() chama de verdade?

    Perguntas frequentes

    O que é o problema do diamante?
    É o cenário em que uma classe herda de duas que, por sua vez, herdam de uma base comum, formando um diagrama em losango. A dúvida é qual método vale em caso de conflito e quantas vezes a base comum é inicializada. A MRO do Python resolve isso visitando cada classe uma única vez, em ordem determinística.
    Como vejo a ordem de resolução de uma classe?
    Use Classe.__mro__ ou Classe.mro(), que devolvem a lista das classes na ordem de busca, da mais específica para a mais geral, terminando em object. Ler essa lista é a forma definitiva de entender por que um método foi escolhido, sem depender de intuição sobre a hierarquia.
    A ordem em que listo as bases muda o comportamento?
    Muda. Em class D(B, C), a MRO prefere B a C, então o método de B vence quando ambos definem o mesmo nome. Inverter para class D(C, B) inverte a preferência. Por isso a ordem das bases é uma decisão de projeto que afeta qual versão de cada método a sua classe usa.
    Por que sempre chamar super() nos métodos das bases?
    Porque é o super() em cada classe que encadeia as chamadas ao longo da MRO, garantindo que cada uma contribua sua parte uma vez. Se uma classe da cadeia esquecer o super(), ela interrompe o encadeamento e classes seguintes deixam de ser inicializadas, um bug sutil em hierarquias com herança múltipla.
    Herança múltipla é sempre uma má ideia?
    Não, desde que usada com critério. A forma saudável são os mixins: classes pequenas de comportamento, combinadas com a classe principal. O que costuma dar errado é tentar modelar, por herança múltipla, algo que seria melhor por composição. A próxima aula trata justamente de quando preferir composição a herança.

    Fontes

    Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.