🌗 ← Índice

🐍 Python P12 — Herança e Polimorfismo


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.




🎯 O que vamos aprender


  • Herança: classe filha herda da classe pai
  • `super()` — chamar métodos da classe pai
  • Override: sobrescrever métodos para mudar comportamento
  • Polimorfismo: mesma interface, comportamentos diferentes
  • Herança múltipla (com cuidado!) e MRO
  • Classes abstratas com `abc`



  • 🧬 Herança — a classe filha


    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.



    🔑 `super()` — chamar o construtor do pai


    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 — mesma chamada, comportamentos diferentes


    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.



    📐 Herança Múltipla e MRO (com cuidado!)


    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.



    🔒 Classes Abstratas com `abc`


    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.



    ✍️ Exercícios (15 min)


  • E1: Crie `Funcionario` (nome, salario) com método `bonus()` que retorna 0. Crie `Gerente(Funcionario)` que sobrescreve `bonus()` retornando 10% do salário. Teste os dois.
  • E2: Crie `ContaBancaria` (titular, _saldo) com `depositar()`, `sacar()` e `ver_saldo()`. Crie `ContaPoupanca(ContaBancaria)` que adiciona `render_juros(taxa)` (multiplica o saldo). Use `super().__init__`.
  • E3: Usando polimorfismo, crie uma função `apresentar(animal)` que chama `animal.falar()` e `animal.descrever()`. Teste com `Cachorro` e `Gato`.



  • 🧪 Desafio (15 min)


    Crie um sistema de funcionários de uma empresa:


  • Classe abstrata `Funcionario` (ABC) com `nome`, `salario` e `@abstractmethod calcular_pagamento()`
  • `CLT(Funcionario)`: paga o salário fixo
  • `PJ(Funcionario)`: paga salário + 15% (não tem benefícios, recebe mais)
  • `Estagiario(Funcionario)`: paga 60% do salário base
  • Função `processar_pagamento(func)` que chama `calcular_pagamento()` de qualquer um
  • Teste com 1 de cada e mostre o total da folha

  • Extra: adicione um `Relatorio` que recebe a lista de funcionários e imprime nome + pagamento de todos (use polimorfismo na listagem).




    ✅ Correção (10 min)


    Mostrar gabarito E1
    
    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
    

    Mostrar gabarito E2
    
    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
    

    Mostrar gabarito E3
    
    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
    

    Mostrar gabarito Desafio
    
    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
    



    📌 Resumo da aula


    ConceitoO que éExemplo
    HerançaFilha reutiliza código do paiclass Carro(Veiculo):
    super()Chama método/construtor do paisuper().__init__(marca, ano)
    OverrideSobrescrever método pra mudardef falar(self): return "Au au!"
    PolimorfismoMesma chamada, comportamentos diferentesforma.area()
    MROOrdem de busca em herança múltiplaClasse.__mro__
    ABCClasse 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. ✨


    ← Voltar ao índice