Guia de estudo

06/05/2026 · Aula 09

Polimorfismo

A mesma ordem. Reações diferentes.

Programação Orientada a Objetos com Python
Tecnologia em Análise e Desenvolvimento de Sistemas

Por que polimorfismo existe? Polimorfismo por Herança Duck Typing Dunder Methods

plano de voo

O que vamos aprender hoje

01

O Problema

Por que código sem polimorfismo se torna um pesadelo.

02

O Conceito

O que é polimorfismo de verdade, sem jargão.

03

As Formas

Polimorfismo por herança e Duck Typing — quando usar cada um.

04

Na Prática

Sistemas reais que usam polimorfismo sem você perceber.

conexão com a aula anterior

Você já fez a base. Hoje vê o resultado.

Na aula passada aprendemos a sobrescrever métodos. Hoje vemos o que essa habilidade habilita.

  • 🔙 Aula 08 — Sobrescrita: Subclasses podem redefinir métodos da classe pai com suas próprias implementações.
  • ➡️ Hoje — Polimorfismo: Quando chamamos o mesmo método em objetos diferentes, cada um executa a sua versão. Esse comportamento tem um nome.

A Relação Direta

Sobrescrita (override) é o mecanismo. Polimorfismo é o resultado que enxergamos de fora.

começando pelo problema

Imagine um sistema sem polimorfismo

Você precisa calcular o salário de três tipos de funcionário. Sem polimorfismo, o código começa assim:

  • 😬 Para cada tipo novo de funcionário, você precisa abrir esse código e adicionar mais um elif.
  • 💥 Se esquecer de atualizar, o sistema silenciosamente calcula errado — sem erro, sem aviso.

Regra prática

Se você tem if/elif verificando tipos de objetos para decidir o que fazer — seu código está pedindo polimorfismo.

o problema em código

O if/elif que nunca para de crescer

Veja o que acontece quando você tem 3 tipos de funcionário e não usa polimorfismo.

🔴
O parâmetro tipo existe só para compensar a falta de polimorfismo.
🔴
Cada novo tipo de funcionário = editar essa função. Para sempre.
sem_polimorfismo.py
class Clt:
    def __init__(self, salario_base):
        self.salario_base = salario_base

class Freelancer:
    def __init__(self, valor_hora, horas):
        self.valor_hora = valor_hora
        self.horas = horas

class Socio:
    def __init__(self, lucro, percentual):
        self.lucro = lucro
        self.percentual = percentual

# Toda a lógica centralizada aqui — e presa aqui para sempre
def calcular_pagamento(funcionario, tipo):
    if tipo == 'clt':
        return funcionario.salario_base
    elif tipo == 'freelancer':
        return funcionario.valor_hora * funcionario.horas
    elif tipo == 'socio':
        return funcionario.lucro * funcionario.percentual
    # novo tipo? precisa editar aqui. sempre.

a solução

E se cada objeto soubesse calcular o próprio salário?

A ideia do polimorfismo é simples: em vez de uma função central que sabe tudo, cada objeto carrega sua própria lógica.

  • 💡 Pergunta-chave: Por que a função calcular_pagamento precisa saber como cada tipo de funcionário é pago?
  • 🎯 Resposta: Não precisa. Cada classe deveria ser responsável pelo seu próprio comportamento.

O princípio

Mova a lógica para dentro da classe. Deixe o objeto responsável por saber o que fazer quando você mandar a mesma ordem.

a solução em código

Cada classe sabe calcular o próprio salário

Movemos a lógica para dentro de cada classe. Todas têm o mesmo método — mas cada uma implementa do seu jeito.

Cada classe tem o mesmo nome de métodocalcular_salario() — mas com lógica própria.
Nenhuma classe sabe da existência das outras. Cada uma é responsável só por si mesma.
com_polimorfismo.py
class Clt:
    def __init__(self, salario_base):
        self.salario_base = salario_base
    def calcular_salario(self):
        return self.salario_base

class Freelancer:
    def __init__(self, valor_hora, horas):
        self.valor_hora = valor_hora
        self.horas = horas
    def calcular_salario(self):
        return self.valor_hora * self.horas

class Socio:
    def __init__(self, lucro, percentual):
        self.lucro = lucro
        self.percentual = percentual
    def calcular_salario(self):
        return self.lucro * self.percentual

o resultado do polimorfismo

Agora veja o que acontece do lado de fora

Com o polimorfismo no lugar, o código que usa os objetos fica radicalmente mais simples — e nunca mais precisa ser alterado.

🟢
O loop não sabe e não precisa saber qual tipo de funcionário está processando.
🟢
Quer adicionar um novo tipo? Crie a classe com calcular_salario() e adicione na lista. Zero mudança no loop.
usando_polimorfismo.py
funcionarios = [
    Clt(3000),
    Freelancer(80, 40),
    Socio(50000, 0.05),
    Clt(4500),            # novo clt? só adicionar na lista
]

# Mesmo comando para todos. Cada um responde do seu jeito.
for f in funcionarios:
    print(f.calcular_salario())

# Saída:
# 3000
# 3200
# 2500.0
# 4500

definição

Agora podemos dar o nome

Polimorfismo é quando você manda a mesma ordem para objetos diferentes, e cada um obedece do seu jeito — sem você precisar saber com quem está falando.

  • 🎭 Poly = muitos. Morphos = formas. Muitas formas de responder à mesma mensagem.
  • 📌 Na prática: mesmo nome de método em classes diferentes. Cada objeto executa a sua versão quando chamado.

As três camadas do polimorfismo

1️⃣ Cada classe tem o método com o mesmo nome.

2️⃣ Você cria uma lista com objetos de tipos diferentes.

3️⃣ Você chama o método em todos — sem if, sem perguntar quem é quem.

antes e depois · lado a lado

A diferença que o polimorfismo faz

❌ Sem Polimorfismo

  • Função central cheia de if/elif
  • Novo tipo = editar código existente
  • Lógica espalhada fora dos objetos
  • Fácil esquecer de um caso

✅ Com Polimorfismo

  • Loop simples, sem condicionais
  • Novo tipo = nova classe, nada mais
  • Cada objeto carrega sua própria lógica
  • Impossível esquecer: a classe define

exemplo do cotidiano

Polimorfismo existe no mundo real

Não é um conceito abstrato. Você já interagiu com polimorfismo hoje.

  • ▶️ Botão Play: Funciona no Spotify, no YouTube, no Netflix. O botão é o mesmo — cada plataforma executa do seu jeito.
  • 💳 Maquininha de cartão: Aceita crédito, débito, Pix, VR. O processo de 'pagar' é o mesmo comando — cada bandeira tem sua lógica interna.

A analogia

Você, como usuário, manda a mesma ordem. O sistema não te pergunta 'qual bandeira você quer usar para processar?'. Ele simplesmente processa — cada objeto do jeito que sabe.

as duas formas de polimorfismo em python

Existem dois caminhos para chegar lá

Em Python, você pode ter polimorfismo com ou sem herança. Cada abordagem tem seu estilo e seu uso ideal.

  • 🌳 Por Herança: Classes compartilham um ancestral comum. O pai 'promete' que o método existe. Filhos entregam a versão deles.
  • 🦆 Duck Typing: Sem parentesco. Se o objeto tem o método que você precisa, ele funciona. Python não verifica a família.

Qual usar?

Herança quando os objetos têm parentesco lógico real (Cão é Animal). Duck Typing quando objetos não relacionados precisam responder ao mesmo comando.

polimorfismo por herança

Caminho 1: A Hierarquia como Contrato

A classe pai define que o método deve existir. Cada filha entrega a sua versão. O polimorfismo acontece quando você usa qualquer objeto da família.

  • 📜 Contrato: A classe pai declara o método. Isso é uma 'promessa' de que todos os filhos vão ter esse método disponível.
  • 🔀 Especialização: Cada filha sobrescreve com sua lógica própria — sem mudar o nome, sem mudar a assinatura.

Regra crítica

O nome do método precisa ser idêntico em todas as classes da hierarquia. É o nome que garante a interface comum.

herança · exemplo clássico

Animais com sons diferentes

Todos são animais. Todos 'falam'. Cada um fala do seu jeito.

🎯
O loop não sabe e não precisa saber se é cachorro ou gato. Só manda falar().
Quer adicionar Leão? Crie a classe Leao(Animal) com falar(). O loop já funciona com ela — sem alterar nada.
animais.py
class Animal:
    def falar(self):
        pass  # a classe pai apenas declara que o método existe

class Cachorro(Animal):
    def falar(self):
        print("Au! Au!")

class Gato(Animal):
    def falar(self):
        print("Miau!")

class Papagaio(Animal):
    def falar(self):
        print("Quem vive, vê!")

# Polimorfismo em ação:
animais = [Cachorro(), Gato(), Papagaio(), Cachorro()]

for animal in animais:
    animal.falar()  # mesma ordem — reações diferentes

herança · formas geométricas

Formas com áreas diferentes

O exemplo clássico da literatura de POO — funciona muito bem para mostrar o conceito.

⚠️
A classe Forma base retorna 0 — subclasses que esquecerem de implementar area() vão herdar esse zero silenciosamente.
🔜
Prévia — Aula 10: Classes Abstratas (ABC) vão forçar que toda subclasse implemente o método, ou o Python vai reclamar.
formas.py
class Forma:
    def area(self):
        return 0  # ⚠️ provisório — Aula 10 resolve com ABC

class Quadrado(Forma):
    def __init__(self, lado):
        self.lado = lado
    def area(self):
        return self.lado ** 2

class Circulo(Forma):
    def __init__(self, raio):
        self.raio = raio
    def area(self):
        return 3.14 * (self.raio ** 2)

class Triangulo(Forma):
    def __init__(self, base, altura):
        self.base = base
        self.altura = altura
    def area(self):
        return (self.base * self.altura) / 2

formas = [Quadrado(4), Circulo(3), Triangulo(6, 4)]

for f in formas:
    print(f"Área: {f.area()}")  # mesma chamada, resultados diferentes

por que isso importa

O que o polimorfismo entrega

🚀

Extensível por natureza

Adicione um novo tipo criando uma nova classe. O restante do sistema não precisa ser tocado.

🧹

Zero if/elif de tipo

Não existe mais 'se é X faça isso, se é Y faça aquilo'. Os objetos se viram sozinhos.

🧩

Desacoplamento real

Quem usa os objetos não precisa conhecer os detalhes internos de cada um. Só precisa saber o nome do método.

duck typing — caminho 2

Python não liga para família. Liga para comportamento.

Duck Typing é a filosofia Python de polimorfismo. Não importa de onde o objeto veio. Importa se ele sabe fazer o que você precisa.

  • 🦆 A frase que define: 'Se caminha como um pato e faz quack como um pato — então é um pato.' Python não verifica a certidão de nascimento.
  • 🔍 Em código: Python não checa se o objeto pertence a uma hierarquia. Ele simplesmente tenta chamar o método. Se funcionar — funcionou.

A diferença para Java ou C#

Nessas linguagens, para ter polimorfismo você obrigatoriamente precisa herdar de uma classe base ou implementar uma interface. Em Python, basta ter o método.

duck typing · o clássico do pato

Objetos sem parentesco — comportamento igual

Pato e Pessoa não têm nenhuma relação de herança. Mas ambos sabem fazer quack — e isso é suficiente.

🦆
Pessoa não herda de Pato. Não existe classe base em comum. Não importa.
O Python só pergunta uma coisa: 'esse objeto tem o método quack()?' Se sim, executa.
duck_typing.py
class Pato:
    def quack(self):
        print("Quack! Quack!")

class Pessoa:
    def quack(self):
        print("Eu estou imitando um pato!")

# Nenhuma herança. Nem precisamos.
# Se tem quack() — funciona.
def faz_quack(objeto):
    objeto.quack()

faz_quack(Pato())    # Quack! Quack!
faz_quack(Pessoa())  # Eu estou imitando um pato!

duck typing · sistema real

Pagamentos sem herança obrigatória

Três formas de pagar. Nenhuma herança. Puro Duck Typing.

🧩
Quer adicionar Cripto? Crie a classe com pagar(). A função finalizar_venda já funciona com ela.
📌
A função finalizar_venda não sabe — e não precisa saber — qual tipo de pagamento está recebendo.
pagamentos_duck.py
class Pix:
    def pagar(self, valor):
        print(f"QR Code gerado — R$ {valor:.2f}")

class CartaoCredito:
    def __init__(self, final):
        self.final = final
    def pagar(self, valor):
        print(f"Cartão {self.final} autorizado — R$ {valor:.2f}")

class Boleto:
    def pagar(self, valor):
        print(f"Boleto gerado — R$ {valor:.2f} (vence em 3 dias)")

# Nenhuma herança. Todos têm pagar(). Isso basta.
def finalizar_venda(metodo, total):
    metodo.pagar(total)

finalizar_venda(Pix(), 150.00)
finalizar_venda(CartaoCredito("1234"), 300.00)
finalizar_venda(Boleto(), 89.90)

duck typing · cuidado

O que acontece se o método não existir?

Duck Typing confia que o objeto tem o método. Se não tiver — Python lança um erro. Você precisa saber lidar com isso.

  • 💥 AttributeError: É o erro que Python lança quando você tenta chamar um método que o objeto não tem.
  • 🛡️ Como se proteger: Existem duas abordagens — verificar antes de chamar (hasattr) ou tentar e tratar o erro (try/except).

Não é fraqueza — é escolha

Em código bem escrito com Duck Typing, você sabe quais objetos vai receber. O tratamento de erro é para casos externos — APIs, dados de usuário, plugins.

duck typing · tratamento de erro

As duas formas seguras de usar Duck Typing

LBYL verifica antes. EAFP tenta e trata. O Python prefere EAFP — mas LBYL tem seu lugar.

🔍
LBYL com hasattr(): Mais explícito. Bom quando receber um objeto sem o método é um caso comum esperado.
🐍
EAFP com try/except: O estilo preferido do Python. Mais conciso. Bom quando o erro é uma exceção rara.
duck_typing_seguro.py
# LBYL — Look Before You Leap
# Verifica se o método existe antes de chamar
def finalizar_venda_segura(metodo, total):
    if hasattr(metodo, 'pagar'):
        metodo.pagar(total)
    else:
        print(f"Erro: {type(metodo).__name__} não tem método pagar()")

# EAFP — Easier to Ask Forgiveness than Permission
# Tenta executar e trata o erro se acontecer
def finalizar_venda_pythonica(metodo, total):
    try:
        metodo.pagar(total)
    except AttributeError:
        print(f"Erro: {type(metodo).__name__} não sabe pagar()")

comparativo técnico

Herança vs Duck Typing — quando usar cada um

Polimorfismo por Herança

  • 🌳 Objetos têm parentesco lógico real ('Cachorro É UM Animal')
  • 🌳 Você quer garantir que todos tenham o método
  • 🌳 Sistemas grandes com equipes diferentes
  • 🌳 Ex: Animal, Veículo, Funcionário

Duck Typing

  • 🦆 Objetos não têm parentesco, mas compartilham comportamento
  • 🦆 Você quer máxima flexibilidade e menos burocracia
  • 🦆 Scripts, automações, sistemas de plugins
  • 🦆 Ex: qualquer coisa que tenha pagar(), enviar(), processar()

polimorfismo que você já usa

Dunder Methods: Python usa polimorfismo o tempo todo

Funções nativas do Python como len(), print() e str() são polimórficas. Funcionam com qualquer objeto que implemente o método correspondente.

  • 📏 len(lista), len(string), len(dicionário): Funcionam porque todos implementam __len__. Duck Typing nativo do Python.
  • 📝 print(objeto): Funciona em qualquer objeto porque Python chama __str__ — que você já aprendeu a sobrescrever.

O que são Dunder Methods?

Métodos com duplo underscore (__) que integram sua classe ao comportamento nativo do Python. Ao implementar __len__, sua classe passa a ser compatível com len() — sem herdar nada.

dunder methods na prática

Fazendo sua classe falar Python nativo

Ao implementar __len__, sua classe passa a responder à função nativa len() — exatamente como listas e strings.

🔌
Você não criou um método tamanho() ou quantidade(). Você fez sua classe falar a língua nativa do Python.
🧩
Isso é Duck Typing em nível de linguagem: len() funciona em tudo que tem __len__ — lista, string, dicionário, e agora Turma.
dunder_len.py
class Turma:
    def __init__(self, alunos):
        self.alunos = alunos

    def __len__(self):
        return len(self.alunos)

    def __str__(self):
        return f"Turma com {len(self)} alunos"

aula = Turma(['Ana', 'Bruno', 'Carla', 'Diego'])

print(len(aula))   # 4  — Python chamou __len__ internamente
print(aula)        # Turma com 4 alunos  — Python chamou __str__

conhecendo antes de evitar

O que é isinstance()?

É uma função nativa do Python que verifica se um objeto pertence a uma determinada classe. Parece útil — e às vezes é. Mas tem uma armadilha.

📌
isinstance(obj, Classe) retorna True ou False. Útil para validar entrada de dados.
🚨
Quando usado para decidir o que fazer com o objeto, vira o mesmo problema do if/elif de tipo que vimos no início da aula.
isinstance_exemplo.py
class Cachorro:
    def falar(self):
        print("Au!")

class Gato:
    def falar(self):
        print("Miau!")

dog = Cachorro()

# isinstance() pergunta: 'esse objeto é dessa classe?'
print(isinstance(dog, Cachorro))  # True
print(isinstance(dog, Gato))      # False

# O problema: usar isso para decidir o que fazer
def fazer_falar(animal):
    if isinstance(animal, Cachorro):
        animal.falar()
    elif isinstance(animal, Gato):
        animal.falar()
    # novo animal? precisa abrir aqui e adicionar mais um elif

boas práticas

O sinal de alerta: isinstance() dentro de lógica

Se você está usando isinstance() para decidir o que fazer com um objeto, é sinal que o polimorfismo não está completo.

  • 🚫 O problema: isinstance(obj, Cachorro) pergunta 'quem você é?'. Polimorfismo não pergunta — só manda agir.
  • A solução: Mova a lógica condicional para dentro de cada classe. Chame obj.agir() e deixe cada objeto saber o que 'agir' significa.

Onde isinstance() é válido

Validação de entrada de dados (verificar se o usuário passou o tipo certo), debugging e logging de erros. O problema é quando isinstance() substitui polimorfismo em lógica de negócio.

resumo de conceitos

Glossário da Aula 09

item detalhe
Polimorfismo Mesma chamada de método em objetos diferentes — cada um responde com sua própria implementação.
Interface Comum O nome do método que todos os objetos compartilham. É o 'contrato' informal que permite o polimorfismo.
Duck Typing Estilo Python: não importa a classe do objeto, importa se ele tem o método que você precisa.
Dunder Method Método com __ duplo que integra sua classe ao comportamento nativo Python (len, str, comparações etc).
Late Binding Python decide qual método chamar no momento da execução — por isso listas heterogêneas funcionam.
EAFP Easier to Ask Forgiveness than Permission — estilo Pythonico de tentar e tratar o erro, em vez de verificar antes.

laboratório em sala · duplas

Exercício ao Vivo: Sistema de Notificações

Em duplas. 20 minutos. O objetivo é implementar um sistema de notificações usando Duck Typing — sem herança obrigatória.

1

Crie os canais

Classes Email, SMS e PushNotification. Cada uma com método enviar(msg) que imprime uma mensagem diferente.

2

Crie o disparador

Função alerta_geral(canais, mensagem) que percorre a lista e chama enviar() em cada canal. Sem if. Sem isinstance.

3

Adicione resiliência

Coloque um try/except AttributeError no disparador para que um canal sem enviar() não quebre o sistema — apenas pule e avise.

4

Teste com objeto inválido

Adicione um objeto qualquer na lista (ex: um número inteiro) e confirme que o sistema sobrevive sem travar.

resolução · laboratório em sala

Passo 1 — Criando os três canais

Cada canal é uma classe independente com o método enviar(msg). Nenhuma herança. Só Duck Typing.

  • 📧 Email: imprime a mensagem formatada como e-mail com remetente fictício.
  • 📱 SMS: imprime com limite de caracteres simulado e prefixo de operadora.
  • 🔔 PushNotification: imprime como notificação de app com emoji de alerta.

O que importa neste passo

As três classes não têm nenhum parentesco entre si. O único ponto em comum é o nome do método: enviar(msg). Isso é suficiente para o Duck Typing funcionar.

resolução · passo 1

Código: Os Três Canais

Classes independentes, mesmo método, saídas diferentes.

Três classes. Zero herança. Cada uma sabe o que 'enviar' significa para ela.
📌
O SMS já tem lógica própria (limite de 50 chars) — exatamente o ponto do polimorfismo: cada um resolve do seu jeito.
notificacoes_canais.py
class Email:
    def enviar(self, msg):
        print(f"📧 [EMAIL] De: sistema@app.com")
        print(f"         Mensagem: {msg}")

class SMS:
    def enviar(self, msg):
        resumo = msg[:50] + '...' if len(msg) > 50 else msg
        print(f"📱 [SMS] {resumo}")

class PushNotification:
    def enviar(self, msg):
        print(f"🔔 [PUSH] ⚠️ Alerta: {msg}")

resolução · passo 2

Código: O Disparador e o Teste Inicial

A função alerta_geral não sabe — e não precisa saber — qual tipo de canal está recebendo.

🔄
O loop trata todos os canais da mesma forma. Cada objeto sabe o que fazer quando enviar() é chamado.
Quer adicionar WhatsApp? Crie a classe com enviar() e adicione na lista. Zero mudança na função.
notificacoes_disparador.py
def alerta_geral(canais, mensagem):
    print(f"--- Disparando alerta para {len(canais)} canal(is) ---")
    for canal in canais:
        canal.enviar(mensagem)  # Duck Typing puro — sem if, sem isinstance
    print("--- Fim do disparo ---")

canais = [
    Email(),
    SMS(),
    PushNotification(),
    Email(),
]

alerta_geral(canais, "Servidor em manutenção às 23h")

resolução · passo 3 e 4

Código: Resiliência e Teste com Objeto Inválido

Adicionando try/except para que um canal quebrado não derrube o sistema inteiro.

🛡️
O try/except AttributeError captura qualquer objeto que não tenha enviar() e segue para o próximo.
🔥
O sistema não trava. Um canal inválido gera um aviso e o disparo continua para os demais.
notificacoes_final.py
def alerta_geral(canais, mensagem):
    print(f"--- Disparando alerta para {len(canais)} canal(is) ---")
    for canal in canais:
        try:
            canal.enviar(mensagem)
        except AttributeError:
            nome = type(canal).__name__
            print(f"⚠️  [{nome}] ignorado — não implementa enviar()")
    print("--- Fim do disparo ---")

canais = [
    Email(),
    SMS(),
    42,                 # ← vai falhar
    PushNotification(),
    "texto qualquer",  # ← também vai falhar
]

alerta_geral(canais, "Teste de resiliência")

resolução · saída esperada

Saída Completa do Programa

O que o terminal exibe ao rodar o código final com os objetos inválidos na lista.

Email, SMS e PushNotification executaram normalmente via Duck Typing.
int e str foram capturados pelo except — o sistema sobreviveu sem travar.
saida_esperada.txt
--- Disparando alerta para 5 canal(is) ---
📧 [EMAIL] De: sistema@app.com
         Mensagem: Teste de resiliência
📱 [SMS] Teste de resiliência
⚠️  [int] ignorado — não implementa enviar()
🔔 [PUSH] ⚠️ Alerta: Teste de resiliência
⚠️  [str] ignorado — não implementa enviar()
--- Fim do disparo ---

encerramento

O que dominamos hoje

  • O problema: Código sem polimorfismo cresce com if/elif e se torna frágil.
  • O conceito: Mesma ordem para objetos diferentes — cada um responde do seu jeito.
  • Por herança: Classe pai define o método, filhas entregam sua versão.
  • Duck Typing: Sem herança obrigatória — basta ter o método.
  • Dunder methods: Sua classe integrada ao comportamento nativo do Python.
  • Segurança: hasattr() e try/except para Duck Typing em produção.

exercícios para casa · visão geral

Desafios de Implementação

Do fácil ao expert — construindo polimorfismo camada por camada.

01–02

Nível Fácil

Interface comum em classes simples.

03–04

Nível Médio

Listas heterogêneas e Dunder Methods.

05

Nível Difícil

Sistema de plugins via Duck Typing.

06

Nível Expert

Refatorar if/elif para polimorfismo.

exercícios · fácil

Fácil: Interface Comum

01

Simulador de Sons

Use herança. Crie a classe pai Instrumento com o método tocar(). Depois crie Piano, Violao e Bateria herdando dela — cada uma sobrescreve tocar() com seu próprio som. Percorra uma lista com os três chamando tocar() em cada um.

02

Meios de Transporte

Use Duck Typing — sem herança, sem classe pai. Crie Carro, Aviao e Barco diretamente, cada um com o método mover() imprimindo como se locomove. Percorra uma lista com os três chamando mover().

exercícios · médio

Médio: Pythonic Way

03

Calculadora de Áreas

Use herança. Crie a classe pai Forma com o método area(). Depois crie Circulo(raio), Retangulo(base, altura) e Triangulo(base, altura) herdando dela — cada uma implementa area() com sua fórmula. Por fim, crie a função calcular_area_total(lista) que percorre a lista e soma todas as áreas sem nenhum if.

04

Objeto Medível com __len__

Crie a classe Playlist(nome, musicas) onde musicas é uma lista de strings. Implemente __len__ para que len(playlist) retorne a quantidade de músicas, e __str__ para que print(playlist) exiba algo como Playlist 'Rock Clássico' — 5 músicas.

exercícios · difícil e expert

Avançado: Arquitetura

05

Sistema de Exportação

Use Duck Typing puro — sem herança. Crie PDFExporter e CSVExporter, cada um com o método exportar(dados) que imprime os dados formatados do seu jeito. Crie a função exportar_relatorio(exporter, dados) que chama exportar() sem saber qual classe está recebendo. Bônus: adicione um try/except AttributeError para o caso de receber um objeto inválido.

06

Refatoração Mestre

Refatore o código abaixo para polimorfismo — cada classe Usuario deve implementar acessar() com sua própria lógica, eliminando todos os if/elif:

def liberar_acesso(usuario, tipo):
  if tipo == 'admin':
    print('Acesso total')
  elif tipo == 'editor':
    print('Acesso a conteúdo')
  elif tipo == 'cliente':
    print('Acesso básico')

revisão final

Resumo da Aula 09 — O essencial

  • Polimorfismo = mesma ordem, reações diferentes.
  • O loop não sabe — e não precisa saber — qual objeto está chamando.
  • Por herança: contrato via hierarquia. Duck Typing: sem hierarquia, só comportamento.
  • Dunder methods fazem sua classe falar a língua nativa do Python.
  • isinstance() em lógica de negócio = polimorfismo incompleto.
  • LBYL (hasattr) e EAFP (try/except) são as duas ferramentas de segurança do Duck Typing — cada uma com seu momento certo.
próxima aula

Aula 10 · 15/05

Classes Abstratas e Interfaces

Lembra do return 0 na classe Forma? Na próxima aula vamos resolver isso de vez com Classes Abstratas — forçando que toda subclasse implemente os métodos necessários.

ABC Module Abstract Methods Formal Interfaces Enforcing Contracts