A função super() é um dos recursos mais importantes e ao mesmo tempo mais mal compreendidos do Python. Presente desde a versão 2.2, ela permite que você chame métodos de classes pai de forma elegante, especialmente em cenários de herança múltipla. Se você já escreveu algo como super().__init__() dentro de uma classe, já usou super() — mas provavelmente ainda não explorou todo o seu potencial.
Neste guia completo, você vai entender o que é a função super(), como ela resolve o problema da ordem de resolução de métodos (MRO), como usá-la em herança única e múltipla, e quais armadilhas evitar. Vamos mergulhar fundo com exemplos práticos que você pode testar no seu próprio ambiente.
O Problema Que super() Resolve
Antes de existir super(), os programadores Python precisavam chamar métodos da classe pai explicitamente pelo nome. Isso funcionava bem para herança simples, mas criava problemas sérios em herança múltipla, especialmente o chamado "problema do diamante" (diamond problem), onde uma classe herda de duas classes que compartilham um ancestral comum.
# O problema: chamada explícita pelo nome da classe
class Pai:
def metodo(self):
print("Método da classe Pai")
class Filho(Pai):
def metodo(self):
Pai.metodo(self) # Chamada explícita
print("Método da classe Filho")
Essa abordagem tem três problemas graves:
- Acoplamento forte: você precisa saber o nome exato da classe pai
- Manutenção difícil: se a hierarquia mudar, você precisa atualizar todas as chamadas
- Herança múltipla quebrada: chamadas explícitas ignoram a ordem de resolução de métodos, pulando classes importantes na cadeia
A função super() resolve todos esses problemas de uma só vez, fornecendo uma maneira dinâmica e consciente da hierarquia de classes para delegar chamadas aos métodos ancestrais.
Sintaxe Básica e Funcionamento
A forma mais comum de usar super() é sem argumentos dentro de um método de instância:
class Pai:
def metodo(self):
print("Pai.metodo executado")
class Filho(Pai):
def metodo(self):
super().metodo() # Delega para Pai.metodo
print("Filho.metodo executado")
f = Filho()
f.metodo()
Saída:
Pai.metodo executado
Filho.metodo executado
Quando você chama super() sem argumentos, o Python automaticamente infere a classe atual e a instância. Isso só funciona dentro de métodos de instância que recebem self como primeiro parâmetro. A forma explícita equivalente seria super(Filho, self).metodo().
O objeto retornado por super() não é a classe pai diretamente. Na verdade, ele é um proxy que sabe como percorrer a Method Resolution Order (MRO) para encontrar o próximo método na cadeia de herança.
super() com __init__
O uso mais comum de super() no dia a dia é dentro do método __init__ para inicializar a parte da classe pai. Este padrão é essencial para criar hierarquias de classes limpas e reutilizáveis. Para entender melhor como o __init__ funciona, veja nosso guia sobre métodos mágicos do Python. A documentação oficial do __init__ também detalha como este método é invocado durante a criação de objetos.
class Animal:
def __init__(self, nome):
self.nome = nome
print(f"Animal.__init__: {self.nome}")
class Cachorro(Animal):
def init(self, nome, raca):
super().init(nome) # Chama Animal.init
self.raca = raca
print(f"Cachorro.init: {self.nome}, {self.raca}")
rex = Cachorro("Rex", "Pastor Alemão")
Saída:
Animal.init: Rex
Cachorro.init: Rex, Pastor Alemão
Note que passamos apenas os argumentos que a classe pai precisa. O super() se encarrega de chamar o __init__ correto na hierarquia. Isso mantém cada classe responsável apenas por sua própria inicialização, seguindo o princípio da responsabilidade única.
Entendendo a MRO (Method Resolution Order)
A MRO é o algoritmo que o Python usa para determinar a ordem em que as classes base são pesquisadas quando você chama um método. Em Python 3, ela é baseada no Algoritmo de Linearização C3, garantindo três propriedades importantes:
- Consistência local: uma classe sempre precede suas subclasses
- Preservação de ordem: a ordem das classes base na definição é respeitada
- Monotonicidade: a ordem não muda quando novas classes são adicionadas
Você pode inspecionar a MRO de qualquer classe usando o atributo __mro__ ou o método mro():
class A:
def metodo(self):
print("A")
class B(A):
def metodo(self):
print("B")
class C(A):
def metodo(self):
print("C")
class D(B, C):
pass
print(D.mro)
(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
d = D()
d.metodo() # "B" - respeita a MRO
No exemplo acima, a MRO de D é: D → B → C → A → object. Isso significa que ao chamar super() dentro de B, o Python irá para C, não para A. É isso que torna a herança múltipla cooperativa possível. Veja a documentação oficial sobre herança para mais detalhes.
Herança Múltipla Cooperativa com super()
O verdadeiro poder de super() aparece na herança múltipla. Quando todas as classes da hierarquia usam super() consistentemente, o Python consegue orquestrar a chamada de cada método na ordem correta, mesmo em hierarquias complexas. Este padrão é conhecido como herança múltipla cooperativa (cooperative multiple inheritance) ou chamada em cadeia (chain of delegation).
class Trabalhador:
def __init__(self, nome):
self.nome = nome
print(f"Trabalhador.__init__({self.nome})")
class Gerente(Trabalhador):
def init(self, nome, departamento):
print(f"Gerente.init iniciado para {nome}")
super().init(nome)
self.departamento = departamento
print(f"Gerente.init finalizado: {self.departamento}")
class Programador(Trabalhador):
def init(self, nome, linguagem):
print(f"Programador.init iniciado para {nome}")
super().init(nome)
self.linguagem = linguagem
print(f"Programador.init finalizado: {self.linguagem}")
class TechLead(Gerente, Programador):
def init(self, nome, departamento, linguagem):
print(f"TechLead.init iniciado para {nome}")
super().init(nome, departamento, linguagem)
print("TechLead.init finalizado")
Testando
tl = TechLead("Ana", "Engenharia", "Python")
A MRO garante que Trabalhador.init seja chamado apenas UMA vez
print(TechLead.mro)
Sem o super() cooperativo, Trabalhador.__init__ seria chamado duas vezes, causando duplicação de inicialização e possíveis efeitos colaterais. A MRO garante que cada __init__ na cadeia seja executado exatamente uma vez. Este padrão é explicado em detalhes por Raymond Hettinger em "Python's super() considered super!", uma leitura obrigatória para quem trabalha com hierarquias complexas.
Assinaturas Consistentes (O Padrão *args, **kwargs)
Um dos maiores desafios da herança múltipla cooperativa é lidar com diferentes conjuntos de parâmetros. A solução clássica é usar *args e **kwargs para aceitar qualquer combinação de argumentos e repassá-los via super():
class Veiculo:
def __init__(self, **kwargs):
self.tipo = kwargs.get('tipo', 'genérico')
print(f"Veiculo.__init__: tipo={self.tipo}")
class Motorizado:
def init(self, **kwargs):
self.combustivel = kwargs.get('combustivel', 'gasolina')
print(f"Motorizado.init: combustivel={self.combustivel}")
class Carro(Motorizado, Veiculo):
def init(self, kwargs):
self.marca = kwargs.get('marca', 'desconhecida')
self.modelo = kwargs.get('modelo', 'desconhecido')
print(f"Carro.init: marca={self.marca}, modelo={self.modelo}")
super().init(kwargs)
fusca = Carro(marca="Volkswagen", modelo="Fusca", tipo="terrestre", combustivel="gasolina")
Este padrão com **kwargs é conhecido como cooperative super() call pattern e é amplamente usado em frameworks Python como Django, SQLAlchemy e Flask. Cada classe extrai os argumentos que lhe interessam e repassa o restante adiante na cadeia.
A Forma Com Argumentos: super(Tipo, Objeto)
Embora a forma sem argumentos seja a mais comum, super() também aceita argumentos explícitos: super(Tipo, objeto). Esta forma é útil em cenários mais avançados, como métodos de classe (@classmethod):
class Trabalhador:
@classmethod
def criar(cls, nome):
print(f"Trabalhador.criar chamou para {nome}")
return cls(nome=nome)
class Gerente(Trabalhador):
@classmethod
def criar(cls, nome, departamento="Geral"):
print(f"Gerente.criar chamou para {nome}")
Em @classmethod, super() precisa de cls
instancia = super(Gerente, cls).criar(nome=nome)
instancia.departamento = departamento
return instancia
g = Gerente.criar("Carlos", "TI")
print(f"{g.nome}, {g.departamento}")
Note que dentro de métodos de classe, super() sem argumentos também funciona em Python 3, mas a forma explícita deixa a intenção mais clara. Para métodos estáticos (@staticmethod), super() não pode ser usado, pois não há referência de classe ou instância.
Armadilhas Comuns e Como Evitá-las
1. Esquecer de Chamar super().__init__()
Se você definir __init__ em uma classe filha sem chamar super().__init__(), a classe pai não será inicializada. Isso é uma das causas mais comuns de bugs:
class Pai:
def __init__(self):
self.valor = 42
class Filho(Pai):
def init(self):
pass # Esqueceu de chamar super().init()
f = Filho()
print(f.valor) # AttributeError: 'Filho' object has no attribute 'valor'
2. Ordem das Classes Base na Definição
A ordem em que você lista as classes base na definição da classe determina a MRO. Colocar na ordem errada pode levar a comportamentos inesperados:
class A:
def acao(self): print("A")
class B:
def acao(self): print("B")
class C(A, B): # A tem prioridade
pass
class D(B, A): # B tem prioridade
pass
C().acao() # "A"
D().acao() # "B"
3. Quebrar a Cadeia de super()
Se uma classe na hierarquia não chamar super(), a cadeia é interrompida e nenhuma classe depois dela será executada:
class A:
def metodo(self): print("A")
class B(A):
def metodo(self):
print("B")
Não chamou super().metodo() - cadeia quebrada
class C(B):
def metodo(self):
super().metodo()
print("C")
c = C()
c.metodo()
Apenas "B" e "C" são impressos - "A" nunca é chamado
Casos de Uso Reais
Mixins e Composição
Mixins são classes pequenas que adicionam funcionalidades específicas. Combinados com super(), eles permitem montar comportamentos complexos de forma modular:
class JSONMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class LoggerMixin:
def log(self, mensagem):
print(f"[LOG] {mensagem}")
class Usuario(LoggerMixin, JSONMixin):
def init(self, nome, email):
self.nome = nome
self.email = email
self.log(f"Usuário {nome} criado")
u = Usuario("Ana", "[email protected]")
print(u.to_json())
Frameworks e ORMs
Frameworks como Django e SQLAlchemy usam super() extensivamente em suas hierarquias de classes. Quando você define um modelo no Django chamando super().save(), está participando de uma cadeia cooperativa que gerencia validação, sinais e persistência. A documentação oficial da função super() tem exemplos adicionais que complementam este guia.
super() vs Chamada Explícita: Comparação
Vale a pena ver a diferença na prática entre usar super() e chamar o método da classe pai explicitamente:
class A:
def acao(self): print("A", end=" ")
class B(A):
def acao(self):
A.acao(self) # Chamada explícita - ignora MRO
print("B", end=" ")
class C(A):
def acao(self):
A.acao(self) # Chamada explícita - ignora MRO
print("C", end=" ")
class D(B, C):
def acao(self):
B.acao(self) # Só chama B e A, pula C
print("D", end=" ")
D().acao() # Saída: "A B D" - C foi pulado!
Com super():
class A2:
def acao(self): print("A2", end=" ")
class B2(A2):
def acao(self):
super().acao()
print("B2", end=" ")
class C2(A2):
def acao(self):
super().acao()
print("C2", end=" ")
class D2(B2, C2):
def acao(self):
super().acao()
print("D2", end=" ")
D2().acao() # Saída: "A2 C2 B2 D2" - correta!
A diferença é clara: com chamadas explícitas, você perde o controle fino que a MRO oferece. Com super(), a hierarquia é respeitada.
Conclusão
A função super() é uma ferramenta indispensável para quem trabalha com programação orientada a objetos em Python. Ela não é apenas açúcar sintático para chamar métodos da classe pai, mas sim um mecanismo sofisticado que viabiliza a herança múltipla cooperativa, mantendo o código limpo, modular e fácil de manter.
Os pontos principais que você deve lembrar:
super()segue a MRO, não apenas a classe pai direta- Todas as classes na hierarquia devem usar
super()para que a cooperação funcione - O padrão
*args, **kwargsé essencial para assinaturas flexíveis - Inspecione a MRO com
Classe.__mro__quando tiver dúvidas sobre a ordem de execução
Depois de dominar super(), o próximo passo natural é aprofundar seus conhecimentos em programação orientada a objetos em Python, explorando tópicos como metaclasses, descritores e composição avançada.
Para se aprofundar ainda mais, consulte a PEP 3135 — New Super, que documentou a sintaxe simplificada de super() sem argumentos introduzida no Python 3. O artigo da GeeksForGeeks sobre super() também oferece exemplos complementares, e a discussão no Stack Overflow sobre super() esclarece as dúvidas mais frequentes da comunidade.
Bons estudos e boas codificações! 🐍