Em Python, quando herdamos de uma classe, ganhamos todos os seus comportamentos. Mas nem sempre o comportamento padrão do pai serve para o filho. A sobrescrita é o ato de 'redefinir' esse comportamento.
Diferente de outras linguagens, o Python não permite criar dois métodos com o mesmo nome e argumentos diferentes no mesmo escopo (Overload). Se você definir o mesmo nome duas vezes, a última definição sempre apagará a anterior. Por isso, em Python, focamos 100% no Override (Sobrescrita).
Dica de Ouro: Para o Python, o nome do método é a chave única. Se o nome é igual, houve sobrescrita.
A sobrescrita é a ferramenta que permite que classes filhas tenham comportamentos únicos mantendo a interface (contrato) do pai.
| Técnica | Quando usar | Uso de super() |
|---|---|---|
| Substituição Total | Lógica da filha é 100% diferente. | Não utiliza. |
| Extensão | Filha adiciona passos à lógica base. | Chamada obrigatória. |
Regra de Design: Se você sobrescrever um método e não chamar super(), toda a inteligência da classe pai é perdida para aquele objeto.
A herança múltipla permite que você crie classes 'híbridas'. Imagine que você tem uma classe para 'Autenticação' e outra para 'Log de Auditoria'. Com herança múltipla, você pode criar um 'UsuarioSeguro' que herda ambas as capacidades sem precisar reescrever código.
Visão de Especialista: A herança múltipla em Python é a base do padrão 'Mixin'. Ela deve ser usada para COMPOR comportamentos ortogonais (que não dependem um do outro), e não para criar árvores genealógicas confusas.
class MinhaClasse(Base1, Base2, Base3):
pass O Python aceita qualquer número de bases na declaração. Cada base contribui com seus atributos e métodos para a classe filha, como se fossem encaixados um a um.
Ave tem voar(), Nadador tem nadar().class Pato(Ave, Nadador): pass
# Python busca métodos em: Pato → Ave → Nadador → object
class Pato(Nadador, Ave): pass
# Python busca métodos em: Pato → Nadador → Ave → object Atenção: se duas bases tiverem um método com o mesmo nome, a que aparecer primeiro na declaração 'vence'. Isso é resolvido pelo algoritmo MRO — que veremos logo após o Problema do Diamante.
O nome vem do formato do diagrama de herança. A herda de ninguém. B e C herdam de A. D herda de B e C. Desenhado, o grafo forma um losango — o diamante.
A
/ \
B C
\ /
D B e C herdam e sobrescrevem o mesmo método de A. Quando D chama esse método, o Python precisa decidir qual implementação usar — a de B ou a de C. Sem uma regra clara, isso seria ambíguo e perigoso.
O Python não tem medo do diamante — ele tem um algoritmo determinístico que garante uma ordem de busca única e previsível para qualquer hierarquia. É exatamente isso que veremos no próximo slide.
MRO significa Method Resolution Order — a ordem em que o Python percorre as classes para encontrar um método ou atributo. Ela é calculada uma única vez quando a classe é definida, usando o algoritmo C3 Linearization.
[D][B, A, object][C, A, object][D, B, C, A, object]O C3 adia A porque C ainda não foi visitado. A regra é: uma classe só entra na lista quando nenhuma outra classe que a herda ainda está pendente. Isso garante que B e C (ambos filhos de A) sejam visitados antes de A.
class D(B, C): pass
print(D.__mro__) # D → B → C → A → object
class E(C, B): pass
print(E.__mro__) # E → C → B → A → object A ordem dos pais na declaração é a única coisa que você controla. O C3 faz o resto de forma determinística e segura.
O Python evita ambiguidades de herança múltipla usando estas três premissas do algoritmo C3:
print(MinhaClasse.__mro__)
# OU
print(MinhaClasse.mro()) Programação Orientada a Objetos com Python
Tecnologia em Análise e Desenvolvimento de Sistemas
plano de voo
Revisão dos fundamentos de herança simples e inicialização de atributos.
Dominar a redefinição de comportamentos em subclasses.
Compor objetos a partir de múltiplas bases sem ambiguidade.
O algoritmo de busca de métodos do Python.
recapitulação · parte 1
Herança modela taxonomia. Se a relação for 'possui um', usamos Composição.
recapitulação · parte 2
Vimos que se a filha define __init__, ela esconde o do pai. O super() resolve isso.
class Veiculo:
def __init__(self, marca):
self.marca = marca
class Carro(Veiculo):
def __init__(self, marca, portas):
# Chamada obrigatória para manter o DNA
super().__init__(marca)
self.portas = portas fundamentação
Ao sobrescrever, você decide se quer substituir totalmente o pai ou apenas adicionar novos passos.
delegação avançada
override na prática
Neste sistema de RH, o bônus é calculado de forma diferente dependendo do cargo hierárquico.
class Funcionario:
def __init__(self, salario):
self.salario = salario
def calcular_bonus(self):
return self.salario * 0.10
class Gerente(Funcionario):
def calcular_bonus(self):
# Substitui totalmente o cálculo do pai
return self.salario * 0.25 super() em métodos
Vamos garantir que o Gerente ganhe o bônus padrão de 10% MAIS uma gratificação fixa.
class Gerente(Funcionario):
def calcular_bonus(self):
# Delega o cálculo de 10% para o pai
base = super().calcular_bonus()
return base + 500.00 arquitetura de sistemas
Frameworks como Django e Odoo utilizam esta técnica exaustivamente para injetar funcionalidades (Mixins).
herança múltipla · sintaxe
Antes dos problemas, veja a beleza: uma classe pode herdar de duas bases independentes e ganhar todos os seus comportamentos de uma só vez.
class Filha(Base1, Base2)Pato não implementa nada — herda tudo das duas bases.class Ave:
def voar(self):
print("🐦 Batendo asas...")
class Nadador:
def nadar(self):
print("🏊 Nadando...")
# Pato herda de DUAS bases independentes
class Pato(Ave, Nadador):
pass
donald = Pato()
donald.voar() # Herdado de Ave
donald.nadar() # Herdado de Nadador laboratório prático
Criar uma SmartLamp que herda a física da Lâmpada e a inteligência do Mixin.
prática · passo 1
A base define identidade (marca e modelo) e estado elétrico. O método ligar() é a operação fundamental que as subclasses poderão estender.
class Dispositivo:
def __init__(self, marca, modelo):
self.marca = marca
self.modelo = modelo
self.ligado = False
def ligar(self):
self.ligado = True
print(f"[{self.marca} {self.modelo}] Ligado.")
def status(self):
return "Ligado" if self.ligado else "Desligado" prática · passo 2
O Ar-Condicionado usa o pai para ligar, mas já define uma temperatura de conforto imediatamente.
class ArCondicionado(Dispositivo):
def ligar(self):
super().ligar() # Ativa a energia no pai
self.temp = 22
print(f"Resfriando ambiente para {self.temp}°C") prática · passo 3
Este Mixin não é um dispositivo independente, é uma capacidade plugável de rede.
class ConectavelMixin:
def conectar_wi_fi(self):
print(f"[{self.marca}] Conectado à rede local.")
def enviar_dados(self, dados):
print(f"Sincronizando: {dados}") laboratório · composição
A herança múltipla permite 'montar' o dispositivo com as peças que precisamos.
Herda de Lâmpada (que herda de Dispositivo) para ter brilho e energia.
Herda de ConectavelMixin para ganhar métodos de rede e telemetria.
Uso do MRO para definir se o status vem da lâmpada ou da conexão.
Um objeto único que responde a comandos de voz e interruptores físicos.
laboratório · passo 4
Unindo o mundo físico ao digital. Note a ordem das classes na herança.
class Lampada(Dispositivo):
def status(self):
return f"Luz: {super().status()}"
class SmartLamp(Lampada, ConectavelMixin):
def status(self):
base = super().status()
return f"{base} | Sincronizado: SIM"
obj = SmartLamp("Philips", "Hue")
obj.conectar_wi_fi() conflitos de nomes
Quando duas classes irmãs herdam do mesmo pai e sobrescrevem o mesmo método, qual das duas vence em D(B, C)?
class A:
def identificar(self):
print("Sou A — a raiz")
class B(A):
def identificar(self):
print("Sou B — filho esquerdo")
class C(A):
def identificar(self):
print("Sou C — filho direito")
class D(B, C):
pass
obj = D()
obj.identificar() ciência da computação
O Python não chuta. Ele percorre uma lista ordenada chamada MRO até encontrar o método. Veja a lista de D e acompanhe o raciocínio.
D(B, C) coloca B antes de C na declaração.D(C, B) e o resultado muda: Sou C — filho direito.print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
# Python percorre da esquerda para a direita:
# 1. D → não tem identificar() → continua
# 2. B → tem identificar() → EXECUTA e para
# 3. C, A, object nunca chegam a ser visitados
obj = D()
obj.identificar()
# Sou B — filho esquerdo debug de hierarquia
Sempre verifique a ordem de resolução em casos de herança múltipla para evitar bugs de execução.
print(SmartLamp.__mro__)
# Resultado: (SmartLamp, Lampada, Dispositivo, ConectavelMixin, object) check-list técnico
O interpretador Python nunca erra a ordem. Você também não deveria.
A busca começa sempre no nível mais baixo (subclasse) e sobe lateralmente.
No caso (B, C), B tem prioridade por ser o primeiro argumento declarado.
Toda busca em Python termina na classe object, a raiz de todos os tipos.
guia de arquitetura
Como escolher a estrutura correta para seu sistema?
encerramento · teoria
consolidação
Aplicação prática dos conceitos de Override, super() e Herança Múltipla.
Override puro: substituição total de comportamento.
Extensão com super() em hierarquias reais.
Arquitetura com Mixins seguindo o padrão Smart Home.
Rastreio completo do MRO no grafo do diamante com super() em cadeia.
prática · fácil
Crie Animal com o método falar() imprimindo 'Som genérico'. Implemente subclasses Cachorro e Gato sobrescrevendo falar() com seus respectivos sons. Instancie cada uma e confirme o override.
Crie Pessoa com saudar() retornando 'Hello!'. Implemente Brasileiro(Pessoa) sobrescrevendo saudar() para retornar 'Olá!'. Instancie ambas e confirme que cada uma responde diferente.
prática · médio
Crie ContaBancaria com sacar(valor) que desconta o valor do saldo. Implemente ContaCorrente(ContaBancaria) sobrescrevendo sacar(): use super() para executar o saque normal e depois deduza uma taxa adicional de R$ 2,00. Mostre o saldo antes e depois.
Crie Pessoa com __init__(nome, idade). Implemente Aluno(Pessoa) adicionando o atributo matricula, garantindo a chamada de super().__init__(nome, idade). Instancie um Aluno e imprima todos os seus atributos.
prática · difícil/expert
Crie Dispositivo(marca, modelo) com ligar(). Implemente AgendamentoMixin com agendar(horario) que imprime o horário de ativação. Crie Irrigador(Dispositivo, AgendamentoMixin) e demonstre chamando ligar() e agendar('06:00').
Reimplemente a hierarquia A, B(A), C(A), D(B, C). Em cada identificar(), chame super().identificar() antes de imprimir a própria mensagem. Chame obj.identificar() e observe todos os níveis aparecerem em sequência — o MRO em ação.
A arte de tratar objetos diferentes através de uma interface comum sem herança rígida.