🌗 ← Índice
Data: 06/08/2026
Nível: Intermediário (Mês 2 — POO)
Na aula passada você criou classes e objetos. Hoje vem o que torna a POO poderosa de verdade: herança (reutilizar código de classes existentes) e polimorfismo (o mesmo método se comportando diferente em cada classe). É assim que frameworks gigantes (Django, FastAPI) organizam tudo.
Uma classe filha herda tudo da classe pai: atributos e métodos. Depois ela pode adicionar coisas novas ou modificar as existentes.
class Animal:
def __init__(self, nome):
self.nome = nome
def falar(self):
return "..."
def descrever(self):
return f"{self.nome} é um animal"
class Cachorro(Animal): # (Animal) = herda de Animal
def falar(self): # override: muda o comportamento
return "Au au!"
class Gato(Animal):
def falar(self):
return "Miau!"
rex = Cachorro("Rex")
mingau = Gato("Mingau")
print(rex.falar()) # Au au!
print(mingau.falar()) # Miau!
print(rex.descrever()) # Rex é um animal (herdado!)
💡 O `descrever()` e o `__init__` vieram da classe pai — a filha não precisou reescrever nada. Isso é reutilização: escreveu uma vez, usou em todas.
Quando a filha tem seu próprio `__init__`, ela precisa chamar o `__init__` do pai com `super()` pra não perder os atributos herdados.
class Veiculo:
def __init__(self, marca, ano):
self.marca = marca
self.ano = ano
def descricao(self):
return f"{self.marca} ({self.ano})"
class Carro(Veiculo):
def __init__(self, marca, ano, portas):
super().__init__(marca, ano) # chama o __init__ do Veiculo
self.portas = portas # atributo novo da filha
def descricao(self): # override + super()
base = super().descricao()
return f"{base} — {self.portas} portas"
fusca = Carro("VW", 1974, 2)
print(fusca.descricao()) # VW (1974) — 2 portas
💡 Regra de ouro: toda vez que a filha define o próprio `__init__`, ela DEVE chamar `super().__init__(...)` na primeira linha — senão os atributos do pai nunca são criados.
Polimorfismo = "muitas formas". Você chama o MESMO método (`falar()`, `calcular_area()`) e cada objeto responde do seu jeito. O código que usa os objetos nem precisa saber qual classe é — ele só chama o método e confia.
class Forma:
def area(self):
raise NotImplementedError("Subclasse deve implementar")
class Retangulo(Forma):
def __init__(self, base, altura):
self.base = base
self.altura = altura
def area(self):
return self.base * self.altura
class Circulo(Forma):
def __init__(self, raio):
self.raio = raio
def area(self):
return 3.14159 * self.raio ** 2
# Função polimórfica: aceita QUALQUER forma
def imprimir_area(forma):
print(f"Área: {forma.area():.2f}")
imprimir_area(Retangulo(5, 3)) # Área: 15.00
imprimir_area(Circulo(4)) # Área: 50.27
💡 A função `imprimir_area()` não sabe (nem precisa saber) se recebe um retângulo ou círculo — ela só chama `.area()`. Isso é polimorfismo: código genérico que funciona com qualquer classe que tenha o método.
Python permite herdar de várias classes. Mas com poder vem responsabilidade: quando duas classes pai têm o mesmo método, o Python usa o MRO (Method Resolution Order) — a ordem em que ele procura o método.
class Nadador:
def habilidade(self):
return "nada"
class Piloto:
def habilidade(self):
return "voa"
class PatoNadador(Nadador, Piloto): # Nadador vem primeiro
pass
class PatoVoador(Piloto, Nadador): # Piloto vem primeiro
pass
print(PatoNadador().habilidade()) # nada (Nadador tem prioridade)
print(PatoVoador().habilidade()) # voa (Piloto tem prioridade)
# Ver a ordem de resolução:
print(PatoNadador.__mro__)
⚠️ Dica de especialista: herança múltipla é um dos recursos mais abusados do Python. Regra prática: evite quando possível — prefira composição (uma classe que TEM outra) ou use herança múltipla só com classes "mixins" pequenas e independentes.
Uma classe abstrata é um "contrato": define métodos que TODAS as filhas DEVEM implementar. Ela não pode ser instanciada diretamente.
from abc import ABC, abstractmethod
class Pagamento(ABC): # ABC = classe abstrata
@abstractmethod
def processar(self): # todas as filhas DEVEM ter
pass
class Pix(Pagamento):
def processar(self):
return "Pagamento via Pix processado"
class Cartao(Pagamento):
def processar(self):
return "Pagamento via Cartão processado"
# p = Pagamento() # ❌ TypeError! Classe abstrata não instancia
print(Pix().processar()) # ✅ Pagamento via Pix processado
print(Cartao().processar()) # ✅ Pagamento via Cartão processado
💡 Se uma filha esquecer de implementar `processar()`, o Python recusa instanciar — o erro aparece na hora, não em produção. Isso é um contrato garantido.
Crie um sistema de funcionários de uma empresa:
Extra: adicione um `Relatorio` que recebe a lista de funcionários e imprime nome + pagamento de todos (use polimorfismo na listagem).
class Funcionario:
def __init__(self, nome, salario):
self.nome = nome
self.salario = salario
def bonus(self):
return 0
class Gerente(Funcionario):
def bonus(self):
return self.salario * 0.10
f = Funcionario("Ana", 3000)
g = Gerente("Carlos", 8000)
print(f.bonus()) # 0
print(g.bonus()) # 800.0
class ContaBancaria:
def __init__(self, titular, saldo):
self.titular = titular
self._saldo = saldo
def depositar(self, valor):
if valor > 0:
self._saldo += valor
def sacar(self, valor):
if valor > 0 and valor <= self._saldo:
self._saldo -= valor
return True
return False
def ver_saldo(self):
return self._saldo
class ContaPoupanca(ContaBancaria):
def __init__(self, titular, saldo):
super().__init__(titular, saldo)
def render_juros(self, taxa):
self._saldo += self._saldo * taxa
cp = ContaPoupanca("Maria", 1000)
cp.render_juros(0.01)
print(cp.ver_saldo()) # 1010.0
class Animal:
def __init__(self, nome):
self.nome = nome
def falar(self):
return "..."
def descrever(self):
return f"{self.nome} é um animal"
class Cachorro(Animal):
def falar(self):
return "Au au!"
class Gato(Animal):
def falar(self):
return "Miau!"
def apresentar(animal):
print(animal.falar())
print(animal.descrever())
apresentar(Cachorro("Rex")) # Au au! / Rex é um animal
apresentar(Gato("Mingau")) # Miau! / Mingau é um animal
from abc import ABC, abstractmethod
class Funcionario(ABC):
def __init__(self, nome, salario):
self.nome = nome
self.salario = salario
@abstractmethod
def calcular_pagamento(self):
pass
class CLT(Funcionario):
def calcular_pagamento(self):
return self.salario
class PJ(Funcionario):
def calcular_pagamento(self):
return self.salario * 1.15
class Estagiario(Funcionario):
def calcular_pagamento(self):
return self.salario * 0.60
def processar_pagamento(func):
print(f"Pagando {func.nome}: R$ {func.calcular_pagamento():.2f}")
def folha(funcionarios):
total = 0
for f in funcionarios:
print(f"{f.nome}: R$ {f.calcular_pagamento():.2f}")
total += f.calcular_pagamento()
print(f"Total da folha: R$ {total:.2f}")
ana = CLT("Ana", 3000)
carlos = PJ("Carlos", 8000)
pedro = Estagiario("Pedro", 1500)
processar_pagamento(ana) # Pagando Ana: R$ 3000.00
processar_pagamento(carlos) # Pagando Carlos: R$ 9200.00
folha([ana, carlos, pedro])
# Ana: R$ 3000.00 / Carlos: R$ 9200.00 / Pedro: R$ 900.00
# Total da folha: R$ 13100.00
| Conceito | O que é | Exemplo |
|---|---|---|
| Herança | Filha reutiliza código do pai | class Carro(Veiculo): |
| super() | Chama método/construtor do pai | super().__init__(marca, ano) |
| Override | Sobrescrever método pra mudar | def falar(self): return "Au au!" |
| Polimorfismo | Mesma chamada, comportamentos diferentes | forma.area() |
| MRO | Ordem de busca em herança múltipla | Classe.__mro__ |
| ABC | Classe abstrata = contrato | @abstractmethod |
Próxima aula: Métodos Especiais — `__str__`, `__repr__`, `__len__`, `__eq__` e os "métodos mágicos" que fazem o Python funcionar por baixo dos panos. ✨