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.
Antes de começar
try/finally 6/13 · hierarquia de exceções 10/13. Estava disponível — e ainda assim nem todos acertaram. gabaritaram as 3
acertaram 2 das 3
acertaram 1 das 3
não acertou nenhuma
Diagnóstico da turma
property e ABC, a intenção de validar. _ que guarda o dado, o raise no lugar do return. Onde apertou — teórica
| 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
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.
@temp.setter
def temp(self, valor):
if valor < 10 or valor > 30:
raise ValueError("Fora da faixa")
self._temp = valor @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
O dado mora em self._preco (com underline). O getter lê; o setter valida e guarda. Construído em 4 estágios:
self.preco = preco — funciona, mas não valida nada.self._preco — o _ sinaliza "mexa por dentro, não direto".@property: devolve self._preco (o privado, nunca self.preco — senão recursão).@preco.setter: recebe valor, valida, e armazena em self._preco.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 @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
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.
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 @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
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.
@property
def preco(self):
return self.preco # chama a si mesmo → RecursionError @property
def preco(self):
return self._preco # lê o privado (com underline) Clínica de erros reais (anônimos) · 3 de 5
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.
@preco.setter
def preco(self): # nem recebe 'valor'
if self._preco < 0:
raise ValueError("Valor Negativo")
return # não guarda nada @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
Dois enganos clássicos: trocar -= por =- (vira atribuição de negativo) e usar print onde o __str__ esperava um return.
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 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
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.
def __init__(self, nome, valor, quantidade=100):
self.nome = nome
self.valor = valor
self.quantidade = 100 # ignora o parâmetro e fixa 100 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
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.
class Acervo:
def ItemIndisponivelError(self, item): # virou método
if item == item:
raise ValueError("Item Não Disponivel") 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
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).
ItemAcervo define o contrato; quem não implementar prazo_dias() não instancia.Livro/Revista ficam no nível de cima (não indentadas dentro de outra classe) e herdam de ItemAcervo.listar() usa só print(item) — cada item formata o próprio __str__.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
property e setter: validar com raise vs limitar com clamp.
exceção customizada que herda de Exception.
classe abstrata, herança e polimorfismo (duck typing).
Agora é com vocês — 20 min
Fechando o Bloco 1
Bloco 2 · Mini projeto 2
Carro, Moto e Caminhão — cada um com sua tarifa por hora.
capacidade limitada; controla quem está dentro.
valor = horas × tarifa; e o faturamento total.
O que o sistema precisa fazer
Critério de pronto
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.
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}") 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
__init__: Veiculo (placa, horas) e Estacionamento (capacidade, lista de veículos). self.placa = placa). A classe é o molde; cada veículo é um objeto criado a partir dela. Classes e objetos em Python
Técnica 2 · Representação (__str__)
__str__ para o veículo aparecer como ABC1D23 (Carro) — R$ 15.00 quando você der print() nele: placa, tipo e valor a pagar. if para cada um. Métodos e representação de objetos
Técnica 3 · Encapsulamento
@property e @setter: o setter valida (7 caracteres) e guarda em self._placa; se for inválida, levanta ValueError. self._placa (com underline). Encapsulamento em Python
Técnica 4 · Classe abstrata (ABC)
Veiculo ser abstrata (ABC) e marque tarifa_hora() como @abstractmethod: a base exige o método, mas não o implementa. Veiculo direto dá erro — só Carro, Moto e Caminhão (concretos) podem ser instanciados. Classes abstratas e interfaces
Técnica 5 · Herança e super()
class Carro(Veiculo) herda __init__, a placa, o cálculo e o __str__ — você só escreve a tarifa_hora() de cada tipo. super().__init__(...) para reaproveitar o da classe-mãe em vez de repetir. Herança (parte 1) e Sobrescrita de métodos
Técnica 6 · Polimorfismo
valor_a_pagar() chama tarifa_hora() e cada veículo responde com a sua tarifa — mesma chamada, resultado diferente, sem nenhum if tipo == .... listar(), um único print(item) dentro do laço imprime cada veículo no seu próprio formato (pelo __str__). Polimorfismo
Técnica 7 · Composição
Estacionamento não herda de Veiculo — ele contém uma lista deles. Os métodos entrar, sair e faturamento trabalham sobre essa lista. Construtor __init__, destrutores e composição
Técnica 8 · Exceções
Exception. entrar() deve raise quando lotado; sair() deve raise quando a placa não existe — sempre levantar o erro, nunca return dele. try / except / else / finally para reagir a cada caso (exatamente a Q5 da prova). Tratamento de exceções
Por onde começar
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.
Veiculo abstrata + um Carro concreto. Rode: crie um carro e dê print nele.
Encapsule a placa (property/setter + validação). Teste de propósito com uma placa inválida.
Adicione Moto e Caminhão com suas tarifas. Confirme que o cálculo muda sozinho (polimorfismo).
Crie o Estacionamento (composição) com entrar, sair, faturamento e listar.
Adicione as exceções e um try/except/else/finally no trecho de uso.
💾Entrega: um arquivo estacionamento.py que roda de ponta a ponta, com um trecho de teste no fim mostrando a saída.
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.