Guia de estudo

Aula 16 · Qua, 10/06

Correção da 1ª VA

O que a prova revelou — e como consertar

Aula em dois blocos. Bloco 1: correção técnica da prova, conceito por conceito, com os erros reais da turma. Bloco 2: mini projeto 2.

Bloco 1 · Correção Bloco 2 · Mini projeto Live-coding Sem expor ninguém

Antes de começar

Três dessas questões já estavam no simulado

  • 🎯 Três das dez questões da prova teórica eram idênticas (código e cenário) a questões do simulado que vocês tinham para treinar.
  • 📊 Acertos nelas: lista default mutável 9/13 · fluxo try/finally 6/13 · hierarquia de exceções 10/13. Estava disponível — e ainda assim nem todos acertaram.
  • Quem treinou, colheu: 8 dos 13 acertaram pelo menos 2 das 3. Antes da 2ª VA, refaça o simulado até zerar os erros.

5 alunos

gabaritaram as 3

3 alunos

acertaram 2 das 3

4 alunos

acertaram 1 das 3

1 aluno

não acertou nenhuma

Diagnóstico da turma

Vocês sabem o mapa. Falta a mecânica.

  • 🗺️ Como grupo, a turma acertou o mapa de POO: quais classes criar, os nomes dos métodos, que existe property e ABC, a intenção de validar.
  • ⚙️ O que faltou foi a mecânica — como cada peça funciona por dentro: o par property/setter, o _ que guarda o dado, o raise no lugar do return.
  • ▶️ 10 de 13 entregaram código que não executa; só 3 rodam de ponta a ponta. Quase ninguém rodou o próprio código antes de entregar.
  • 🛠️ Por isso esta aula é técnica e prática: a gente conserta a mecânica ao vivo, com os erros que de fato apareceram — sem nome, sem nota, é o conceito que importa.

Onde apertou — teórica

Mapa de prioridade: o que a turma mais errou

item detalhe
Q7 · property com clamp (max/min) 1/13 — a mais errada da prova
Q8 · exceção customizada (__init__) 4/13
Q5 · try / except / else / finally 6/13 · estava no simulado
Q3 · classe abstrata (asserção-razão) 7/13
Q6 · herança e sobrescrita 7/13
Q4 · property com validação (raise) 8/13
Q2 · parâmetro padrão mutável 9/13 · estava no simulado
Q10 · polimorfismo (duck typing) 9/13
Q1 · __str__ (print e f-string) 10/13
Q9 · hierarquia de exceções 10/13 · estava no simulado

O caso da questão mais errada

Mesmo decorador, comportamentos opostos

Na Q7, 7 alunos marcaram "levanta ValueError" — no reflexo, sem ler o corpo do setter. O decorador é o mesmo das duas; o que muda é o código dentro.

Q4 — valida e LEVANTA

  • Fora da faixa → raise
  • Interrompe o programa
  • Saída: 25, depois ValueError
q4_setter.py
@temp.setter
def temp(self, valor):
    if valor < 10 or valor > 30:
        raise ValueError("Fora da faixa")
    self._temp = valor

Q7 — LIMITA (clamp), não levanta

  • max/min prende na faixa
  • Nunca levanta erro
  • Saída: 10, depois 0
q7_setter.py
@nota.setter
def nota(self, valor):
    self._nota = max(0, min(10, valor))

O erro nº 1 da prova — 9 dos 13 tropeçaram aqui

property e setter são um PAR com o mesmo nome

O dado mora em self._preco (com underline). O getter lê; o setter valida e guarda. Construído em 4 estágios:

1️⃣
Atributo público: self.preco = preco — funciona, mas não valida nada.
2️⃣
Guarda no privado: renomeia para self._preco — o _ sinaliza "mexa por dentro, não direto".
3️⃣
Getter @property: devolve self._preco (o privado, nunca self.preco — senão recursão).
4️⃣
Setter @preco.setter: recebe valor, valida, e armazena em self._preco.
produto_preco.py
class Produto:
    def __init__(self, nome, preco):
        self.nome = nome
        self.preco = preco          # passa pelo setter (valida)

    @property                       # getter: LÊ o dado
    def preco(self):
        return self._preco          # o privado, com underline

    @preco.setter                   # setter: VALIDA e GUARDA
    def preco(self, valor):
        if valor < 0:
            raise ValueError("Preço não pode ser negativo")
        self._preco = valor
Regra de bolso: @property e @x.setter sempre vêm em par, com o mesmo nome do atributo público; por dentro, o valor vive em self._x.

Clínica de erros reais (anônimos) · 1 de 5

@x.setter sem o @property x antes → NameError

Cinco alunos escreveram o setter sem ter declarado a property com o mesmo nome antes. O Python tenta achar quantidade para chamar .setter nela, não encontra, e derruba o arquivo inteiro na importação.

❌ como veio

entregue.py
class Produto:
    def __init__(self, nome, preco, quantidade):
        ...

    @quantidade.setter   # NameError: name 'quantidade' is not defined
    def quantidade(self, valor):
        if valor < 0:
            raise ValueError("Quantidade não pode ser negativa")
        self._quantidade = valor

✅ a correção

corrigido.py
    @property                # 1º o getter — DEFINE o nome 'quantidade'
    def quantidade(self):
        return self._quantidade

    @quantidade.setter       # 2º o setter — agora 'quantidade' existe
    def quantidade(self, valor):
        if valor < 0:
            raise ValueError("Quantidade não pode ser negativa")
        self._quantidade = valor

Clínica de erros reais (anônimos) · 2 de 5

Getter que devolve self.preco → RecursionError

Dentro da property preco, fazer return self.preco chama a própria property de novo — e de novo, para sempre. O dado real está em self._preco.

❌ como veio

  • self.preco chama o getter
  • que chama self.preco…
  • RecursionError: maximum recursion depth exceeded
entregue.py
@property
def preco(self):
    return self.preco     # chama a si mesmo → RecursionError

✅ a correção

  • self._preco é o atributo
  • o getter só LÊ ele
  • sem recursão
corrigido.py
@property
def preco(self):
    return self._preco    # lê o privado (com underline)

Clínica de erros reais (anônimos) · 3 de 5

O setter validou… mas esqueceu de guardar

Três alunos escreveram um setter que checa o valor mas nunca faz self._preco = valor. Resultado: o objeto fica sem o atributo, e qualquer leitura depois quebra.

❌ como veio

entregue.py
@preco.setter
def preco(self):          # nem recebe 'valor'
    if self._preco < 0:
        raise ValueError("Valor Negativo")
    return                # não guarda nada

✅ a correção

corrigido.py
@preco.setter
def preco(self, valor):   # recebe o valor novo
    if valor < 0:
        raise ValueError("Preço não pode ser negativo")
    self._preco = valor   # VALIDA e depois GUARDA

Clínica de erros reais (anônimos) · 4 de 5

O método devolve; quem imprime é quem chamou

Dois enganos clássicos: trocar -= por =- (vira atribuição de negativo) e usar print onde o __str__ esperava um return.

❌ como veio

entregue.py
def vender(self, qtd):
    if qtd > self.quantidade:
        raise ValueError("Estoque insuficiente!")
    venda = self.quantidade =- qtd   # '=-' vira = (-qtd): estoque vira -5
    return venda

def prazo_dias(self):
    print("Prazo: 14 dias")          # imprime; __str__ recebe None

✅ a correção

corrigido.py
def vender(self, qtd):
    if qtd > self.quantidade:
        raise ValueError("Estoque insuficiente!")
    self.quantidade -= qtd           # reduz de verdade

def prazo_dias(self):
    return 14                        # devolve; o __str__ formata

Clínica de erros reais (anônimos) · 5 de 5

Classe é um molde: o dado entra pelo construtor

Construtor que ignora o parâmetro e fixa um número, ou que embute "Caneta" na classe, quebra a ideia de molde. Tudo que chega por parâmetro vira self.x = x.

❌ como veio

entregue.py
def __init__(self, nome, valor, quantidade=100):
    self.nome = nome
    self.valor = valor
    self.quantidade = 100   # ignora o parâmetro e fixa 100

✅ a correção

corrigido.py
def __init__(self, nome, preco, quantidade):
    self.nome = nome
    self.preco = preco
    self.quantidade = quantidade   # guarda o que CHEGOU

Exceção customizada — 9 não criaram

Uma exceção customizada é uma linha

Vários tentaram transformar a exceção em método, ou usar ValueError no lugar. Ela é só uma classe que herda de Exception — e você a levanta depois do loop, quando não achou nada.

❌ tentativas comuns

entregue.py
class Acervo:
    def ItemIndisponivelError(self, item):   # virou método
        if item == item:
            raise ValueError("Item Não Disponivel")

✅ a forma certa

corrigido.py
class ItemIndisponivelError(Exception):
    pass

def emprestar(self, titulo):
    for item in self.itens:
        if item.titulo == titulo:
            self.itens.remove(item)
            return item
    raise ItemIndisponivelError(
        f"Item '{titulo}' não está disponível no acervo")

Questão 2 ao vivo — ABC, herança e polimorfismo

O Acervo inteiro, sem mistério

Três armadilhas que vimos aqui: classes aninhadas por indentação, subclasse que não herda da abstrata, e Acervo herdando de ItemAcervo (é composição, não herança).

🧩
ABC + abstractmethod: ItemAcervo define o contrato; quem não implementar prazo_dias() não instancia.
🌿
Herança real: Livro/Revista ficam no nível de cima (não indentadas dentro de outra classe) e herdam de ItemAcervo.
🦆
Polimorfismo: listar() usa só print(item) — cada item formata o próprio __str__.
questao2.py
from abc import ABC, abstractmethod


class ItemIndisponivelError(Exception):
    pass


class ItemAcervo(ABC):
    def __init__(self, titulo):
        self.titulo = titulo

    @abstractmethod
    def prazo_dias(self): ...

    def __str__(self):
        return f"{self.titulo} (empréstimo: {self.prazo_dias()} dias)"


class Livro(ItemAcervo):
    def prazo_dias(self):
        return 14


class Revista(ItemAcervo):
    def prazo_dias(self):
        return 7


class Acervo:                    # composição: TEM itens (não É um item)
    def __init__(self):
        self.itens = []

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

    def listar(self):
        for item in self.itens:
            print(item)          # polimorfismo via __str__

    def emprestar(self, titulo):
        for item in self.itens:
            if item.titulo == titulo:
                self.itens.remove(item)
                return item
        raise ItemIndisponivelError(
            f"Item '{titulo}' não está disponível no acervo")
Acervo tem uma lista de itens — isso é composição. Herdar de ItemAcervo aqui faria o Acervo virar um "item de acervo", o que não faz sentido.

É o mesmo assunto, em duas roupas

A ponte entre a teórica e a prática

  • 🔗 Os mesmos conceitos caíram nos dois formatos. Quem entende a mecânica acerta os dois; quem decora um padrão erra os dois.

Q4 / Q7 ↔ Produto

property e setter: validar com raise vs limitar com clamp.

Q8 ↔ ItemIndisponivelError

exceção customizada que herda de Exception.

Q3 / Q6 / Q10 ↔ Acervo

classe abstrata, herança e polimorfismo (duck typing).

Agora é com vocês — 20 min

Corrija a SUA prova rodando o código

  • Abra a sua questão 1 e a sua questão 2 no VS Code.
  • Rode cada arquivo (F5 ou python questaoN.py) e LEIA o primeiro traceback: linha, tipo, mensagem.
  • Conserte só o primeiro erro e rode de novo — um de cada vez.
  • Confira o par property/setter: getter lê self._x, setter recebe valor e guarda em self._x.
  • Garanta que o construtor guarda os parâmetros (self.x = x), sem fixar valores.
  • Rode até a saída bater com a esperada do enunciado. Me chame quando travar.

Fechando o Bloco 1

Escreveu um método? Rodou.

  • ▶️ O hábito que mais muda sua nota: rodar o código enquanto escreve, método por método — não só no fim.
  • 🐞 Ler o traceback é uma habilidade: linha, tipo, mensagem. O erro te diz onde olhar.
  • 📌 Na 2ª VA vou pedir a saída do teste colada como comentário no fim do arquivo — isso força a execução.
  • 🚀 A seguir, o Bloco 2 — Mini projeto 2: aplicar tudo isso num sistema do zero.

Bloco 2 · Mini projeto 2

Sistema de Estacionamento — agora é com vocês

  • 🅿️ Você vai construir, do zero, um sistema que controla um estacionamento: veículos entram, ficam algumas horas, pagam por tipo e saem.
  • 🧠 O objetivo não é decorar — é aplicar. Cada peça do sistema usa uma técnica que já vimos; os próximos slides dizem qual técnica e em que aula revisar.
  • ▶️ Regra de ouro (a prova mostrou!): rode o código a cada passo. Comece pequeno, teste, cresça.

Veículos

Carro, Moto e Caminhão — cada um com sua tarifa por hora.

Pátio

capacidade limitada; controla quem está dentro.

Cobrança

valor = horas × tarifa; e o faturamento total.

O que o sistema precisa fazer

Requisitos do projeto

  • Cadastrar veículos de 3 tipos (carro, moto, caminhão), cada um com placa e horas estacionadas.
  • Cada tipo cobra uma tarifa por hora — Carro R$ 5, Moto R$ 3, Caminhão R$ 10; o valor a pagar é tarifa × horas.
  • A placa é validada (7 caracteres) e guardada sempre em maiúsculas.
  • O pátio tem capacidade máxima; um veículo só entra se houver vaga.
  • Dá para tirar um veículo pela placa e calcular o faturamento de quem está no pátio.
  • Erros são tratados: pátio lotado e veículo não encontrado têm mensagens claras.
  • Ao listar, cada veículo se mostra assim: placa, tipo e valor — ex.: ABC1D23 (Carro) — R$ 15.00.

Critério de pronto

Como testar — e a saída esperada

Cole este bloco no fim do seu estacionamento.py. Se a sua saída bater com esta, o sistema está correto. Teste também os extremos: placa inválida deve levantar ValueError, e tirar um veículo fora do pátio deve levantar VeiculoNaoEncontradoError.

Exemplo de teste

teste.py
patio = Estacionamento(capacidade=2)
patio.entrar(Carro("abc1d23", 3))
patio.entrar(Moto("xyz4e56", 2))

print("No pátio:")
patio.listar()
print(f"Faturamento previsto: R$ {patio.faturamento():.2f}")

try:
    patio.entrar(Caminhao("qwe7r89", 5))   # pátio lotado
except VagaIndisponivelError as e:
    print("Erro:", e)
else:
    print("Entrou sem problemas")
finally:
    print("Tentativa finalizada")

saiu = patio.sair("ABC1D23")
print("Saiu:", saiu)
print(f"Faturamento agora: R$ {patio.faturamento():.2f}")

Saída esperada

terminal
No pátio:
ABC1D23 (Carro) — R$ 15.00
XYZ4E56 (Moto) — R$ 6.00
Faturamento previsto: R$ 21.00
Erro: Estacionamento lotado
Tentativa finalizada
Saiu: ABC1D23 (Carro) — R$ 15.00
Faturamento agora: R$ 6.00

Técnica 1 · Classes e objetos

Modele o mundo em classes

  • 🏗️ Comece definindo as classes e o que cada uma guarda no __init__: Veiculo (placa, horas) e Estacionamento (capacidade, lista de veículos).
  • 📦 Tudo que chega pelo construtor vira atributo (self.placa = placa). A classe é o molde; cada veículo é um objeto criado a partir dela.

📍 Aula 02

Classes e objetos em Python

Técnica 2 · Representação (__str__)

Faça cada veículo se descrever

  • 🖨️ Implemente __str__ para o veículo aparecer como ABC1D23 (Carro) — R$ 15.00 quando você der print() nele: placa, tipo e valor a pagar.
  • 💡 Dica: o tipo (Carro/Moto) pode sair do nome da própria classe — sem precisar de um if para cada um.

📍 Aula 03

Métodos e representação de objetos

Técnica 3 · Encapsulamento

Proteja a placa com property e setter

  • 🔒 A placa não pode ser qualquer coisa. Use @property e @setter: o setter valida (7 caracteres) e guarda em self._placa; se for inválida, levanta ValueError.
  • 🧹 Aproveite o setter para normalizar: guardar sempre em maiúsculas.
  • ⚠️ Lembre da prova: property e setter são um par com o mesmo nome; o dado mora em self._placa (com underline).

📍 Aula 04

Encapsulamento em Python

Técnica 4 · Classe abstrata (ABC)

Veiculo é um contrato, não um veículo de verdade

  • 🧩 Ninguém estaciona um "veículo genérico". Faça Veiculo ser abstrata (ABC) e marque tarifa_hora() como @abstractmethod: a base exige o método, mas não o implementa.
  • 🚫 Resultado: criar um Veiculo direto dá erro — só Carro, Moto e Caminhão (concretos) podem ser instanciados.

📍 Aula 10

Classes abstratas e interfaces

Técnica 5 · Herança e super()

Carro, Moto e Caminhão herdam de Veiculo

  • 🌿 class Carro(Veiculo) herda __init__, a placa, o cálculo e o __str__ — você só escreve a tarifa_hora() de cada tipo.
  • 🔁 Se precisar acrescentar algo no construtor de um tipo, chame super().__init__(...) para reaproveitar o da classe-mãe em vez de repetir.

📍 Aulas 07 e 08

Herança (parte 1) e Sobrescrita de métodos

Técnica 6 · Polimorfismo

O mesmo método, comportamentos diferentes

  • 🦆 É o coração do projeto. valor_a_pagar() chama tarifa_hora() e cada veículo responde com a sua tarifa — mesma chamada, resultado diferente, sem nenhum if tipo == ....
  • 📋 No listar(), um único print(item) dentro do laço imprime cada veículo no seu próprio formato (pelo __str__).

📍 Aula 09

Polimorfismo

Técnica 7 · Composição

O pátio TEM veículos (não É um veículo)

  • 🅿️ O Estacionamento não herda de Veiculo — ele contém uma lista deles. Os métodos entrar, sair e faturamento trabalham sobre essa lista.
  • 🔗 Regra para decidir: "é um" → herança; "tem um" → composição. O pátio TEM veículos.

📍 Aula 05

Construtor __init__, destrutores e composição

Técnica 8 · Exceções

Trate o que pode dar errado

  • 💥 Pátio lotado e veículo inexistente não são bugs — são situações previstas. Crie exceções próprias de uma linha (uma para vaga indisponível, outra para veículo não encontrado), cada uma herdando de Exception.
  • 🧯 entrar() deve raise quando lotado; sair() deve raise quando a placa não existe — sempre levantar o erro, nunca return dele.
  • 🔀 No uso do sistema, envolva com try / except / else / finally para reagir a cada caso (exatamente a Q5 da prova).

📍 Aula 14

Tratamento de exceções

Por onde começar

Construa em pequenos passos — e rode cada um

Não escreva tudo de uma vez. Suba uma peça, teste, depois a próxima — esse hábito é o que mais muda a sua nota.

1

Veiculo abstrata + um Carro concreto. Rode: crie um carro e dê print nele.

2

Encapsule a placa (property/setter + validação). Teste de propósito com uma placa inválida.

3

Adicione Moto e Caminhão com suas tarifas. Confirme que o cálculo muda sozinho (polimorfismo).

4

Crie o Estacionamento (composição) com entrar, sair, faturamento e listar.

5

Adicione as exceções e um try/except/else/finally no trecho de uso.

próxima aula

Aula 17 · 17/06

Manipulação de Arquivos

Nossos objetos somem quando o programa fecha. O próximo passo é gravar e ler dados em arquivos: abrir com open(), o bloco with, os modos de leitura e escrita, e fazer a informação persistir.

open() e with ler e escrever (r / w / a) dados que persistem