Guia de estudo

Revisão Prática — 1ª VA (Aulas 01–12)
03/06/2026 · Revisão Prática

Revisão Prática — 1ª VA

Dois programas-âncora que exercitam aulas 01 a 12.

Material de estudo em casa
Cafeteria + Carteira que fecha a conta

POO ponta a ponta ABC, herança, polimorfismo Encapsulamento e composição Exceções customizadas
Revisão Prática — 1ª VA (Aulas 01–12)

como aproveitar

Como usar este material

São dois programas pequenos, completos e propositalmente didáticos. A ideia é você rodar e mexer, não só ler.

01

Leia o slide

Cada bloco de código tem 2 observações curtas que apontam o conceito e a aula correspondente. Foque no que está sublinhado.

02

Rode no seu computador

Os dois códigos completos estão nos guias de estudo (botão 📚). Copie cada um e cole no VS Code ou no Google Colab para rodar. Cada um roda sozinho.

03

Compare com a saída esperada

Cada programa tem um slide com a saída exata do terminal. Se a sua saída diverge, é porque mexeu em algo — ótimo, está aprendendo.

04

Mexa de propósito

Mude um preço, remova o super(), troque o raise por um print: veja o que quebra. Quebrar de propósito é a melhor forma de aprender.

Revisão Prática — 1ª VA (Aulas 01–12)

código 1 · cenário

Cardápio de uma cafeteria

Primeiro programa: um cardápio onde cada item (bebida ou comida) tem nome, preço e sabe se descrever. A classe Cardapio agrupa os itens e calcula o total. Cobre nove aulas — tudo menos exceções.

  • 🧱 Aulas 03, 04 e 05: __str__ para representação amigável, @property para encapsular preço e nome, e Cardapio que compõe uma lista de itens.
  • 🧬 Aulas 07, 08, 09 e 10: Item é abstrata (ABC + @abstractmethod); Bebida e Comida herdam, sobrescrevem descricao() e respondem polimorficamente.
  • Aula 11: Cardapio implementa __len__ — agora len(cardapio) funciona como em qualquer coleção nativa do Python.

O guia de estudo tem o arquivo completo

Clique no botão 📚 deste slide para copiar o cafeteria.py inteiro e rodar.

Revisão Prática — 1ª VA (Aulas 01–12)

código 1 · mapa das classes

O desenho do programa

Antes de ler o código, fixe a estrutura na cabeça. Quatro classes, duas relações.

  • 🎭 Item (ABC) — molde abstrato. Tem _nome, _preco, propriedades de leitura, __str__ concreto e descricao() abstrato.
  • 🥤 Bebida (Item) — herda tudo de Item e adiciona tamanho (P/M/G). Implementa descricao() do seu jeito.
  • 🥐 Comida (Item) — herda de Item e adiciona vegetariana. Implementa descricao() do seu jeito.
  • 📋 Cardapiocompõe uma lista de Itens. Tem adicionar(), total() e __len__ para virar uma coleção pythônica.

Resumo das relações

Bebida e Comida são Item (herança — aula 07). Cardapio tem uma lista de Item (composição — aula 05).

Revisão Prática — 1ª VA (Aulas 01–12)

código 1 · parte 1

Item — a classe abstrata

Começa pelo contrato: todo item do cardápio precisa ter nome, preço, propriedades de leitura e um método descricao() que cada subclasse implementa do seu jeito.

🎭
Abstração (aula 10): ABC + @abstractmethod tornam Item uma classe que não pode ser instanciada direto. Subclasses são obrigadas a implementar descricao().
🔒
Encapsulamento (aula 04): _nome e _preco são protegidos; @property dá leitura sem permitir escrita. Validação acontece no __init__.
cafeteria.py
from abc import ABC, abstractmethod


class Item(ABC):
    """Contrato comum a tudo que pode entrar no cardápio."""

    def __init__(self, nome, preco):
        if preco < 0:
            raise ValueError("preco nao pode ser negativo")
        self._nome = nome
        self._preco = preco

    @property
    def nome(self):
        return self._nome  # somente leitura

    @property
    def preco(self):
        return self._preco

    @abstractmethod
    def descricao(self):
        """Cada subclasse descreve o item do seu jeito."""
        pass

    def __str__(self):
        return f"{self._nome} — R$ {self._preco:.2f}"
Revisão Prática — 1ª VA (Aulas 01–12)

código 1 · parte 2

Bebida e Comida — herança + sobrescrita

Duas filhas de Item. Cada uma adiciona um atributo próprio e dá a sua versão de descricao(). O super().__init__ garante que nome e preço são inicializados pelo pai.

🧬
Herança (aula 07): Bebida e Comida herdam de Item e chamam super().__init__ na primeira linha do construtor — a Regra de Ouro do super().
🎭
Sobrescrita (aula 08): cada filha dá sua versão de descricao(). Essa diferença é o que torna o polimorfismo possível mais à frente.
cafeteria.py
class Bebida(Item):
    def __init__(self, nome, preco, tamanho):
        super().__init__(nome, preco)  # delega ao pai
        self._tamanho = tamanho  # "P", "M" ou "G"

    def descricao(self):
        return (
            f"Bebida {self._nome} ({self._tamanho}) — R$ {self._preco:.2f}"
        )


class Comida(Item):
    def __init__(self, nome, preco, vegetariana=False):
        super().__init__(nome, preco)
        self._vegetariana = vegetariana

    def descricao(self):
        marca = "vegetariana" if self._vegetariana else "tradicional"
        return f"Comida {self._nome} ({marca}) — R$ {self._preco:.2f}"
Revisão Prática — 1ª VA (Aulas 01–12)

código 1 · parte 3

Cardapio — composição + protocolo Sized

O Cardapio cria e guarda sua própria lista de itens (composição). Implementar __len__ faz len(cardapio) funcionar — sem herdar nada, só plugando no protocolo nativo.

🧱
Composição (aula 05): self._itens = [] nasce dentro do cardápio. Se o cardápio some, a lista some junto — ciclo de vida compartilhado.
Sobrecarga de operador (aula 11): __len__ é um dunder do protocolo Sized. Implementou? Ganhou len(cardapio) de graça, sem herdar de ninguém.
cafeteria.py
class Cardapio:
    """Guarda itens e calcula o total. Compoe Itens por dentro."""

    def __init__(self):
        self._itens = []

    def adicionar(self, item):
        self._itens.append(item)

    def total(self):
        # generator expression somando precos
        return sum(item.preco for item in self._itens)

    def __len__(self):
        # protocolo Sized — agora len(cardapio) funciona
        return len(self._itens)
Revisão Prática — 1ª VA (Aulas 01–12)

código 1 · parte 4

main — polimorfismo no laço

Aqui o programa roda. Montamos o cardápio, percorremos os itens chamando o mesmo descricao() em cada um — e cada classe responde do seu jeito. Também demonstramos que Item abstrata barra instanciação.

🎭
Polimorfismo (aula 09): mesma chamada item.descricao() dispara comportamentos diferentes em Bebida e Comida. Late binding na prática.
🛑
Abstração ao vivo (aula 10): Item("...", 5) levanta TypeError. Pegamos com try/except só para mostrar a mensagem sem matar o programa.
cafeteria.py
# Programa principal
# tentar instanciar a classe abstrata estoura TypeError (aula 10)
try:
    Item("Generico", 5.0)
except TypeError as erro:
    print(f"[abstrata] {erro}")

cardapio = Cardapio()
cardapio.adicionar(Bebida("Cafe", 6.50, "M"))
cardapio.adicionar(Bebida("Suco de laranja", 9.00, "G"))
cardapio.adicionar(Comida("Pao de queijo", 4.00, vegetariana=True))
cardapio.adicionar(Comida("Coxinha", 7.50, vegetariana=False))

print("Itens do cardapio:")
for item in cardapio._itens:
    print(f"  - {item.descricao()}")

print(f"Total de itens: {len(cardapio)}")
print(f"Total a pagar: R$ {cardapio.total():.2f}")
Revisão Prática — 1ª VA (Aulas 01–12)

código 1 · saída

Saída esperada do Código 1

Rode o programa e veja exatamente isso no terminal. A primeira linha vem do try/except ao redor da abstrata; o resto é o cardápio sendo descrito polimorficamente.

🛑
A linha [abstrata] é a mensagem de TypeError que o Python gera (em inglês — tracebacks não são traduzidos). A redação exata varia com a versão: no Python 3.12+ ela diz without an implementation for abstract method. O importante é reconhecer que é um TypeError de instanciação de classe abstrata.
🎭
Bebida e Comida imprimem formatos diferentes na mesma chamada — polimorfismo visível na saída.
terminal
[abstrata] Can't instantiate abstract class Item without an implementation for abstract method 'descricao'
Itens do cardapio:
  - Bebida Cafe (M) — R$ 6.50
  - Bebida Suco de laranja (G) — R$ 9.00
  - Comida Pao de queijo (vegetariana) — R$ 4.00
  - Comida Coxinha (tradicional) — R$ 7.50
Total de itens: 4
Total a pagar: R$ 27.00
Revisão Prática — 1ª VA (Aulas 01–12)

código 2 · cenário

Fechar a conta com Carteira

Segundo programa, independente do primeiro: uma Carteira que recusa pagar quando o saldo não cobre o valor da conta. Cobre a aula 12 inteira — raise, try/except/else/finally e exceções customizadas.

  • 📁 Programa autossuficiente: o fechar_conta.py traz as próprias classes e trabalha com valores em reais. Não depende de nenhum outro arquivo.
  • 🚨 Exceção customizada: SaldoInsuficienteError carrega saldo, total e falta, e formata uma mensagem precisa. Quem captura sabe exatamente o que ofereceu.
  • 🧰 Quarteto completo: try envolve o pagamento, except mostra o erro, else celebra o caminho feliz e finally garante que o saldo final aparece — com erro ou sem erro.

O guia de estudo tem o arquivo completo

Clique no botão 📚 deste slide para copiar o fechar_conta.py inteiro e rodar.

Revisão Prática — 1ª VA (Aulas 01–12)

código 2 · parte 1

SaldoInsuficienteError — exceção com dados

Herdar de Exception já bastaria, mas a exceção fica muito mais útil quando carrega os dados do erro. saldo, total e falta ficam disponíveis pra quem captura.

🚨
Exceção customizada (aula 12): herda de Exception e adiciona campos do domínio. super().__init__(mensagem) entrega o texto ao print(erro).
🔗
Carrega os dados do erro (aula 12): saldo, total e falta ficam guardados na exceção; quem captura usa erro.falta direto.
fechar_conta.py
class SaldoInsuficienteError(Exception):
    """Levantada quando a carteira nao cobre o total da conta."""

    def __init__(self, saldo, total):
        self.saldo = saldo
        self.total = total
        self.falta = total - saldo
        super().__init__(
            f"Faltam R$ {self.falta:.2f} para pagar R$ {total:.2f}"
        )
Revisão Prática — 1ª VA (Aulas 01–12)

código 2 · parte 2

Carteira — encapsulamento + raise

A Carteira protege o saldo (_saldo + @property) e usa raise para barrar operações inválidas na origem. Quem chamou decide o que fazer com o erro.

🔒
Encapsulamento (aula 04): _saldo é interno; @property saldo dá leitura sem permitir escrita direta. Validação acontece nos métodos públicos.
🚨
raise na origem (aula 12): pagar detecta o problema e dispara SaldoInsuficienteError — não devolve None, não imprime aviso. Quem chamou é obrigado a tratar.
fechar_conta.py
class Carteira:
    def __init__(self, saldo_inicial=0.0):
        if saldo_inicial < 0:
            raise ValueError("saldo inicial nao pode ser negativo")
        self._saldo = saldo_inicial

    @property
    def saldo(self):
        return self._saldo

    def depositar(self, valor):
        if valor <= 0:
            raise ValueError("valor deve ser positivo")
        self._saldo += valor
        return self._saldo

    def pagar(self, total):
        if total > self._saldo:
            raise SaldoInsuficienteError(self._saldo, total)
        self._saldo -= total
        return self._saldo
Revisão Prática — 1ª VA (Aulas 01–12)

código 2 · parte 3

main — try / except / else / finally

Aqui aparece o quarteto completo. Uma tentativa que falha (conta cara), uma que tem deposito inválido e uma que dá certo (conta barata). O finally garante saldo final sempre.

🧰
Quarteto (aula 12): try envolve só a linha arriscada; except mais específico primeiro; else só roda se nada estourar; finally roda sempre.
🔎
erro.falta lê o dado guardado na exceção — a mensagem fica precisa, não genérica. É a vantagem prática de criar exceção customizada com campos.
fechar_conta.py
# Programa principal
carteira = Carteira(20.00)

total_caro = 27.00  # conta que estoura o saldo

print(f"Saldo inicial: R$ {carteira.saldo:.2f}")
print(f"Total da conta cara: R$ {total_caro:.2f}")

try:
    carteira.pagar(total_caro)
except SaldoInsuficienteError as erro:
    print(f"❌ Pagamento recusado: {erro}")
    print(f"   Deposite mais R$ {erro.falta:.2f} para fechar a conta.")
except ValueError as erro:
    print(f"❌ Valor invalido: {erro}")
else:
    print("Pagamento concluido (caminho feliz).")
finally:
    print(f"Saldo apos tentativa: R$ {carteira.saldo:.2f}")

try:
    carteira.depositar(-10)
except ValueError as erro:
    print(f"❌ Deposito recusado: {erro}")

total_barato = 10.50

try:
    carteira.pagar(total_barato)
except SaldoInsuficienteError as erro:
    print(f"❌ {erro}")
else:
    print(f"✅ Pagamento OK. Sobrou R$ {carteira.saldo:.2f}.")
finally:
    print(f"Saldo final: R$ {carteira.saldo:.2f}")
Revisão Prática — 1ª VA (Aulas 01–12)

código 2 · saída

Saída esperada do Código 2

Saída do programa rodado em sequência: pagamento caro recusado (saldo intacto), depósito inválido recusado, pagamento barato aprovado. finally imprime o saldo final.

O pagamento caro falhou e o saldo NÃO mudou: a validação no pagar() levantou a exceção antes de mexer em _saldo. Estado consistente.
🧹
O finally rodou duas vezes: uma para cada bloco try. Por isso o saldo aparece após cada tentativa, mesmo quando houve exceção.
terminal
Saldo inicial: R$ 20.00
Total da conta cara: R$ 27.00
❌ Pagamento recusado: Faltam R$ 7.00 para pagar R$ 27.00
   Deposite mais R$ 7.00 para fechar a conta.
Saldo apos tentativa: R$ 20.00
❌ Deposito recusado: valor deve ser positivo
✅ Pagamento OK. Sobrou R$ 9.50.
Saldo final: R$ 9.50
Revisão Prática — 1ª VA (Aulas 01–12)

autoavaliação

Você está pronto para a 1ª VA se consegue...

  • Criar uma classe abstrata com ABC + @abstractmethod e explicar por que Item() direto estoura TypeError. (aula 10)
  • Encapsular um atributo com _atributo + @property e dizer por que preco aqui é somente leitura. (aula 04)
  • Escrever uma subclasse que chama super().__init__ e sobrescreve um método — sem esquecer a Regra de Ouro do super(). (aulas 07 e 08)
  • Reconhecer polimorfismo no laço for item in cardapio._itens e explicar por que item.descricao() chama versões diferentes. (aula 09)
  • Distinguir herança de composição: Bebida é Item; Cardapio tem uma lista de Item. (aulas 05 e 07)
  • Implementar __len__ e explicar que isso é sobrecarga de operador / protocolo, não herança. (aula 11)
  • Criar uma exceção customizada que herda de Exception e carrega dados — e dizer por que super().__init__(mensagem) está lá. (aula 12)
  • Usar raise na origem em vez de devolver None ou imprimir aviso — e justificar a diferença. (aula 12)
  • Montar o quarteto try/except/else/finally e explicar o papel de cada um, na ordem correta dos except. (aula 12)
  • Confirmar que uma operação inválida não muda o estado do objeto — saldo continua íntegro depois da exceção. (aulas 04 e 12)