Programas não devem morrer no primeiro tropeço nem mascarar erros devolvendo valores mágicos (None, -1, False). A sacada é separar quem detecta o erro (raise) de quem trata (try/except).
Disjuntor da casa: o raise desarma na primeira sobrecarga; o try/except é você no quadro de luz decidindo o que fazer. except: pass é colar fita isolante no disjuntor.
| Palavra | Quando roda | Para quê |
|---|---|---|
| try | Sempre tenta primeiro | Marca o bloco arriscado. |
| except | Se houver exceção compatível | Plano B; pode mirar tipo específico. |
| else | Se o try terminou sem exceção | O caminho feliz, separado. |
| finally | Sempre, com ou sem erro | Limpeza (fechar arquivo, conexão). |
Exceções são classes com herança: BaseException no topo, Exception como mãe de quase todas. Capturar a mãe pega as filhas — except LookupError pega IndexError e KeyError. Coloque os except específicos antes dos genéricos.
class SaldoInsuficienteError(Exception):
def __init__(self, saldo, valor):
self.falta = valor - saldo
super().__init__(f"Faltam R$ {self.falta:.2f}")
try:
raise SaldoInsuficienteError(saldo=100, valor=150)
except SaldoInsuficienteError as erro:
print(erro) # Faltam R$ 50.00
print(erro.falta) # 50 Arquivo é memória que sobrevive ao programa (fica no disco, não na RAM). Todo arquivo segue o ritual abrir → usar → fechar. O with fecha sozinho — é o finally da aula 14 automático.
| Modo | O que faz | Cuidado |
|---|---|---|
| "r" | Lê (padrão). | FileNotFoundError se não existe. |
| "w" | Escreve. | APAGA todo o conteúdo antes! |
| "a" | Anexa no fim. | Preserva o que já existe e cria se faltar. |
from pathlib import Path
import json
caminho = Path(__file__).parent / "pedidos.json" # ao lado do script
class Pedido:
def __init__(self, cliente, sabor):
self.cliente = cliente
self.sabor = sabor
def to_dict(self):
return {"cliente": self.cliente, "sabor": self.sabor}
@classmethod
def from_dict(cls, dados):
return cls(dados["cliente"], dados["sabor"])
p = Pedido("Ana", "Calabresa")
with open(caminho, "w", encoding="utf-8") as f:
json.dump(p.to_dict(), f, ensure_ascii=False, indent=2) Texto com ; quebra se o dado tiver ; no meio. JSON aguenta vírgula, acento e ponto-e-vírgula sem quebrar — mas grava dicionário, não objeto: converta com to_dict().
| Coleção | Ordem? | Muda? | Repete? |
|---|---|---|---|
| list [ ] | Sim (índice) | Sim | Sim |
| tuple ( ) | Sim (índice) | Não | Sim |
| dict { k: v } | Por chave | Sim | Chaves não |
| set set( ) | Não | Sim | Não |
[ expressão for item in coleção if condição ] — a expressão decide o que entra, o if (opcional) filtra. Dois usos: TRANSFORMAR e FILTRAR.
nomes = [pet.nome for pet in pets] # transformar
caes = [pet for pet in pets if pet.especie == "Cão"] # filtrar Pegadinha: { } vazio é DICIONÁRIO, não set. Para set vazio, use set().
Padrão de projeto é solução pronta, testada e nomeada para um problema recorrente de design OO. É a receita, não o prato. GoF (Gang of Four) é o livro de 1994 que catalogou 23 padrões em 3 famílias: criacionais, estruturais e comportamentais.
class ConfigSistema:
_instancia = None
def __new__(cls):
if cls._instancia is None:
cls._instancia = super().__new__(cls)
cls._instancia.tema = "claro"
return cls._instancia
config1 = ConfigSistema()
config2 = ConfigSistema()
print(config1 is config2) # True — prova com is, não com == __new__ roda ANTES do __init__ e cria o objeto; o Singleton o intercepta. Prove com is (identidade). Se podem existir vários, NÃO é Singleton — é variável global disfarçada, use com parcimônia.
| Padrão | Quem decide a classe | GoF? |
|---|---|---|
| Simple Factory | Uma função central, por um parâmetro. | Não (idiom). |
| Factory Method | A subclasse do criador, sobrescrevendo o método. | Sim. |
# Simple Factory: o dicionário guarda CLASSES; o () instancia
def criar_pizza(sabor):
tabela = {"calabresa": Calabresa, "portuguesa": Portuguesa}
return tabela[sabor]() O dicionário guarda a CLASSE (sem parênteses); esquecer o () devolve a classe, não o objeto. Nem todo if vira Factory — só quando o 'como criar' varia por contexto.
Comportamentais cuidam de COMO os objetos se comunicam depois de já existirem. Imagem-âncora: criacionais montam o elenco, comportamentais ensaiam a peça. Ambos vivem do tripé ABC + polimorfismo + composição.
Um Subject (Observável) mantém uma lista de Observers e, ao mudar de estado, avisa TODOS chamando o mesmo método (atualizar()) em cada um. Relação um-para-muitos.
class Canal:
def __init__(self, nome):
self.nome = nome
self.inscritos = []
def inscrever(self, i):
self.inscritos.append(i)
def publicar(self, video):
for inscrito in self.inscritos:
inscrito.atualizar(video) Cada algoritmo vira uma classe trocável com um método comum; um Contexto guarda a estratégia ativa e delega a ela, trocável em runtime. A key do sorted (aula 18) É a estratégia, e o sorted é o Contexto.
class Carrinho: # Contexto
def __init__(self, total, desconto):
self.total = total
self.desconto = desconto
def total_final(self):
return self.desconto.calcular(self.total)
carrinho.desconto = BlackFriday() # troca em runtime Não confunda: Strategy tem UMA estratégia ativa e DELEGA; Observer tem MUITOS e AVISA todos. Todo Subject com inscrever() deveria ter cancelar() (senão a lista só cresce).
Depois de 21 aulas de terminal, o código ganha janela com Tkinter (já vem com o Python). Frase-âncora: widget é objeto; janela é composição; callback é Observer. Convenção: import tkinter as tk, nunca from tkinter import *.
# ✗ executa na montagem e registra None (botão morto)
tk.Button(janela, text="Ok", command=ao_clicar())
# ✓ passa a função como valor
tk.Button(janela, text="Ok", command=ao_clicar) A segunda armadilha: misturar pack e grid no MESMO container levanta TclError: cannot use geometry manager grid inside . which already has slaves managed by pack. Regra: um gerente por container.
A lista Python é a fonte da verdade; o Listbox é só o espelho. Toda mudança na lista chama preencher_lista() (que limpa com delete(0, tk.END) antes de reinserir).
A persistência sobe um degrau: sai do .txt (aulas 17/23) e entra num banco relacional SQLite (também já vem com o Python, num único arquivo .db). O .txt vira TABELA: cada objeto uma linha, cada atributo uma coluna.
import sqlite3
from pathlib import Path
BANCO = Path(__file__).parent / "loja.db"
conexao = sqlite3.connect(BANCO)
conexao.execute("CREATE TABLE IF NOT EXISTS produtos (id INTEGER PRIMARY KEY AUTOINCREMENT, nome TEXT NOT NULL, preco REAL NOT NULL)")
conexao.commit()
conexao.close() | Operação | SQL |
|---|---|
| Create | INSERT INTO produtos (nome, preco) VALUES (?, ?) |
| Read | SELECT ... FROM produtos WHERE nome LIKE ? |
| Update | UPDATE produtos SET preco = ? WHERE id = ? |
| Delete | DELETE FROM produtos WHERE id = ? |
class Produto:
@classmethod
def from_row(cls, linha): # tupla → objeto (parente do from_dict)
id, nome, preco = linha
return cls(nome, preco, id)
def to_params(self): # objeto → tupla para os ? (sem o id)
return (self.nome, self.preco) O padrão Repositório junta todo o acesso ao banco numa classe (salvar, listar, buscar_por_id, atualizar, remover). Encapsulamento (aula 04): a GUI da 22/23 consome sem nunca ver SQL.
A 2ª VA cobre tudo, mas o foco é o que veio depois da 1ª VA: exceções, arquivos, coleções, padrões de projeto, Tkinter e SQLite. A base de POO (01–12) sustenta tudo por baixo.
O caminho de hoje
Sete blocos novos, agrupados por família. Cada bloco com a ideia, o código mínimo e a armadilha clássica.
raise, try/except/else/finally, hierarquia e exceções customizadas.
open, with, encoding utf-8, modos r/w/a, pathlib e JSON.
list/tuple/dict/set, comprehension, sorted(key=) e sets de objetos.
Padrões criacionais: uma instância só; quem decide qual classe criar.
Padrões comportamentais: avisar muitos × delegar a um algoritmo trocável.
Janela, widgets, command= (Observer), Listbox, messagebox e App(tk.Tk).
Banco relacional, CRUD com placeholder ?, ORM manual e Repositório.
Bloco 14 · Exceções
raise para sinalizar; quem chamou decide a reação com try/except. raise desarma na origem; o try/except é você no quadro de luz decidindo o que fazer. None ou imprimir aviso mascara o erro: ele estoura longe da causa, três telas depois, num AttributeError. Marca o bloco arriscado que pode levantar uma exceção.
Captura a exceção levantada e executa o plano B. Mire um tipo específico.
Roda só se o try terminou sem exceção — o caminho feliz.
Roda SEMPRE, com erro ou sem. Ideal para limpeza (fechar recurso).
Bloco 14 · Exceções
Exceções são classes com herança: capturar a mãe (LookupError) pega as filhas (IndexError, KeyError). E uma exceção sua pode carregar dados do erro.
# hierarquia: IndexError é filha de LookupError (aula 14)
try:
lista = [1, 2, 3]
print(lista[10])
except LookupError as erro: # LookupError pega IndexError e KeyError
print(f"Capturado por LookupError: {type(erro).__name__}")
# Capturado por LookupError: IndexError
# exceção customizada carrega dados do erro (aula 14)
class SaldoInsuficienteError(Exception):
def __init__(self, saldo, valor):
self.falta = valor - saldo
super().__init__(f"Faltam R$ {self.falta:.2f}") # texto vai pro print(erro)
try:
raise SaldoInsuficienteError(saldo=100, valor=150)
except SaldoInsuficienteError as erro:
print(erro) # Faltam R$ 50.00
print(erro.falta) # 50 Bloco 14 · Exceções
| item | detalhe |
|---|---|
| raise | Levanta uma exceção de propósito. Interrompe a função e sobe o erro para quem chamou. |
| as erro | Guarda o objeto capturado. print(erro) mostra a mensagem; erro.falta lê dados customizados. |
| Exceção customizada | Classe que herda de Exception. O nome (SaldoInsuficienteError) já documenta o erro do domínio. |
| Ordem dos except | Específicos primeiro, genéricos por último — senão a classe-mãe captura antes da filha. |
| except: pass | Anti-padrão (engolir o erro). Some da tela, mas o bug continua vivo. Nunca use. |
| Nunca capture BaseException | Inclui KeyboardInterrupt e SystemExit. Capture Exception ou algo mais específico. |
Bloco 17 · Arquivos
O with fecha o arquivo sozinho (é o finally da aula 14 automático). encoding="utf-8" é obrigatório para o português; "w" apaga tudo. Quatro formas de ler: read() (tudo), readline() (uma linha, com o \n), readlines() (lista) e o pythônico for linha in f.
# with abre e fecha sozinho (aula 17) — é o finally da aula 14 automático
with open("pedidos.txt", "w", encoding="utf-8") as f: # "w" cria e APAGA tudo
f.write("Ana;Calabresa;39.90\n") # write não pula linha sozinho
with open("pedidos.txt", "a", encoding="utf-8") as f: # "a" anexa e preserva
f.write("Bruno;Portuguesa;42.00\n")
# as 4 formas de LER (aula 17) — read() traz o arquivo TODO numa string só:
with open("pedidos.txt", "r", encoding="utf-8") as f:
print(repr(f.readline())) # 'Ana;Calabresa;39.90\n' (UMA linha, COM o \n)
with open("pedidos.txt", "r", encoding="utf-8") as f:
print(f.readlines()) # ['Ana;Calabresa;39.90\n', 'Bruno;Portuguesa;42.00\n'] (lista com todas)
with open("pedidos.txt", "r", encoding="utf-8") as f: # "r" lê (padrão) — a forma pythônica:
for linha in f:
print(linha.strip()) # .strip() tira o \n
# Ana;Calabresa;39.90
# Bruno;Portuguesa;42.00 Bloco 17 · Arquivos
As duas formas persistem objetos em disco. A diferença é robustez: o que acontece quando o dado tem um caractere especial.
Bloco 17 · Arquivos
from pathlib import Path: Path("dados") / "pedidos.txt" monta o caminho; Path(__file__).parent ancora o arquivo ao lado do script. salvar() é de instância; from_dict() costuma ser @classmethod (factory da aula 03). .py. Método de instância: objeto → dicionário, pronto para json.dump.
@classmethod: dicionário → objeto novo, via construtor.
Modo "r" em arquivo que não existe. Trate com try/except (aula 14).
Bloco 18 · Coleções
| item | detalhe |
|---|---|
| list [ ] | Ordenada, mutável, aceita repetidos, tem índice. A coleção do dia a dia. |
| tuple ( ) | Ordenada mas imutável. Para dados fixos: coordenadas, RGB, um registro que não muda. |
| dict { chave: valor } | Mapeia chave → valor. Acesso direto pela chave — o repositório por código. |
| set set( ) | Sem ordem e sem repetição. Elimina duplicatas. Atenção: { } vazio é DICIONÁRIO, use set(). |
Bloco 18 · Coleções
[ expressão for item in coleção if condição ]. A expressão decide o que entra; o if (opcional) filtra. Leia como frase: "o nome, pra cada pet, se for Cão".
# list comprehension: TRANSFORMAR e FILTRAR (aula 18)
class Pet:
def __init__(self, nome, especie):
self.nome = nome
self.especie = especie
pets = [Pet("Rex", "Cão"), Pet("Mimi", "Gato"), Pet("Bob", "Cão")]
nomes = [pet.nome for pet in pets] # TRANSFORMAR
print(nomes) # ['Rex', 'Mimi', 'Bob']
caes = [pet.nome for pet in pets if pet.especie == "Cão"] # FILTRAR
print(caes) # ['Rex', 'Bob'] Bloco 18 · Coleções
sorted() devolve lista nova; .sort() ordena in-place e devolve None. .get() evita KeyError. set só deduplica objetos com __eq__ e __hash__.
numeros = [3, 1, 2]
ordenada = sorted(numeros) # sorted() devolve uma lista NOVA (aula 18)
print(ordenada) # [1, 2, 3]
resultado = numeros.sort() # .sort() ordena in-place e devolve None
print(resultado) # None
print(numeros) # [1, 2, 3]
estoque = {"P01": "Café", "P02": "Chá"}
print(estoque.get("P01")) # Café
print(estoque.get("P99")) # None — .get() evita KeyError
print(estoque.get("P99", "vazio")) # vazio
# set só deduplica objetos com __eq__ + __hash__ (aula 11 revisitada na 18)
class Pet:
def __init__(self, nome):
self.nome = nome
def __eq__(self, outro):
return self.nome == outro.nome
def __hash__(self):
return hash(self.nome)
unicos = {Pet("Rex"), Pet("Rex"), Pet("Mimi")}
print(len(unicos)) # 2 Bloco 20 · Padrões criacionais
Garante UMA única instância no programa. Controla quantas existem (sempre uma).
Uma função central decide QUAL classe criar por um parâmetro. Idiom, não é GoF.
A SUBCLASSE do criador decide o produto. A forma GoF do padrão.
Bloco 20 · Padrões criacionais
O Singleton intercepta __new__ (que roda ANTES do __init__) para devolver sempre a mesma instância. Prova-se com is (identidade), não com ==.
# Singleton: intercepta __new__ para devolver sempre a MESMA instância (aula 20)
class ConfigSistema:
_instancia = None
def __new__(cls):
if cls._instancia is None:
cls._instancia = super().__new__(cls)
cls._instancia.tema = "claro" # estado inicial só na 1ª vez
return cls._instancia
config1 = ConfigSistema()
config2 = ConfigSistema()
print(config1 is config2) # True — prova com is (identidade), não com ==
config1.tema = "escuro"
print(config2.tema) # escuro — é o mesmo objeto Bloco 20 · Padrões criacionais
Na Simple Factory, uma FUNÇÃO decide a classe por um parâmetro. No Factory Method (GoF), a SUBCLASSE do criador é quem decide o produto.
from abc import ABC, abstractmethod
class Calabresa:
def __str__(self): return "Calabresa"
class Portuguesa:
def __str__(self): return "Portuguesa"
# Simple Factory: uma função central decide QUAL classe pelo parâmetro (aula 20)
def criar_pizza(sabor):
tabela = {"calabresa": Calabresa, "portuguesa": Portuguesa} # guarda CLASSES
if sabor not in tabela:
raise ValueError(f"Sabor desconhecido: {sabor}")
return tabela[sabor]() # o () instancia a classe escolhida
print(criar_pizza("calabresa")) # Calabresa
# Factory Method (GoF): a SUBCLASSE do criador decide o produto
class Pizzaria(ABC):
@abstractmethod
def criar_pizza(self):
...
def entregar(self):
return f"Entregando {self.criar_pizza()}"
class PizzariaPaulistana(Pizzaria):
def criar_pizza(self):
return Calabresa()
print(PizzariaPaulistana().entregar()) # Entregando Calabresa Bloco 21 · Padrões comportamentais
Os dois são comportamentais e vivem do mesmo tripé (ABC, polimorfismo, composição). A diferença é a intenção: avisar muitos × delegar a um.
Bloco 21 · Padrões comportamentais
O Subject (Canal) guarda a lista de inscritos e, ao publicar, chama atualizar() em cada um. Cada Observer implementa o mesmo método.
from abc import ABC, abstractmethod
# Observer: o Subject AVISA todos os inscritos ao mudar de estado (aula 21)
class Inscrito(ABC):
@abstractmethod
def atualizar(self, video): # cada Observer implementa atualizar()
...
class Canal: # Subject / Observável
def __init__(self, nome):
self.nome = nome
self.inscritos = []
def inscrever(self, inscrito):
self.inscritos.append(inscrito)
def publicar(self, video):
for inscrito in self.inscritos: # notificar = percorrer a lista
inscrito.atualizar(video)
class Fa(Inscrito):
def __init__(self, nome):
self.nome = nome
def atualizar(self, video):
print(f"{self.nome} recebeu: {video}")
canal = Canal("SENAI POO")
canal.inscrever(Fa("Ana"))
canal.inscrever(Fa("Bruno"))
canal.publicar("Aula 21")
# Ana recebeu: Aula 21
# Bruno recebeu: Aula 21 Bloco 21 · Padrões comportamentais
O Contexto (Carrinho) guarda a estratégia ativa e delega a ela. Trocar a estratégia muda o comportamento sem tocar em quem chama. sorted(key=) é o mesmo padrão.
from abc import ABC, abstractmethod
# Strategy: cada algoritmo é uma classe trocável; o Contexto DELEGA (aula 21)
class Desconto(ABC):
@abstractmethod
def calcular(self, valor):
...
class SemDesconto(Desconto):
def calcular(self, valor):
return valor
class BlackFriday(Desconto):
def calcular(self, valor):
return valor * 0.5
class Carrinho: # Contexto: guarda a estratégia ativa
def __init__(self, total, desconto):
self.total = total
self.desconto = desconto
def total_final(self):
return self.desconto.calcular(self.total) # delega à estratégia
carrinho = Carrinho(200, SemDesconto())
print(carrinho.total_final()) # 200
carrinho.desconto = BlackFriday() # troca a estratégia em runtime
print(carrinho.total_final()) # 100.0
# Ponte (aula 18): a key do sorted É a estratégia; sorted é o Contexto
produtos = [{"nome": "Café", "preco": 18}, {"nome": "Chá", "preco": 9}]
print([p["nome"] for p in sorted(produtos, key=lambda p: p["preco"])])
# ['Chá', 'Café'] Bloco 22–23 · Tkinter
mainloop() é o loop de eventos: espera um evento e despacha para o callback registrado. É SEMPRE a última linha. command= de um botão É o Observer da aula 21 — botão = Subject, função = observer, clique = notificação. Use import tkinter as tk, nunca import *. A janela raiz. Toda app tem exatamente UMA.
Lê um Entry — devolve SEMPRE string, mesmo digitando 25. Converta com int().
Geometry managers. UM por container: pack+grid juntos dão TclError.
Bloco 22–23 · Tkinter
A janela raiz, um Label, um Button com command= e o mainloop no fim. O callback reage ao clique — você nunca o chama, o Tk chama.
import tkinter as tk # convenção: nunca from tkinter import * (aula 22)
def ao_clicar(): # callback = o Observer da aula 21
rotulo.config(text="Clicado!")
print("Botão clicado")
janela = tk.Tk() # janela raiz — a app tem exatamente UMA
janela.title("Exemplo")
rotulo = tk.Label(janela, text="Aguardando...") # Label = o print() visual
rotulo.pack(padx=20, pady=10)
# command= recebe a FUNÇÃO como valor (sem parênteses) — clique = notificação
botao = tk.Button(janela, text="Clique", command=ao_clicar)
botao.pack(pady=10)
janela.mainloop() # loop de eventos — SEMPRE a última linha
# ao clicar no botão, o terminal mostra: Botão clicado Bloco 22–23 · Tkinter
A pegadinha que mais cai: os parênteses fazem o Python EXECUTAR a função na montagem, em vez de guardá-la para o clique.
Bloco 22–23 · Tkinter
A app inteira herda de tk.Tk (aula 07): self vira a janela, o estado vira atributo, os callbacks viram métodos. O Listbox espelha a lista Python. Multi-tela usa tkraise (aula 23).
import tkinter as tk
from tkinter import messagebox
# a app inteira herda de tk.Tk (herança da aula 07; padrão da aula 23)
class App(tk.Tk):
def __init__(self):
super().__init__() # obrigatório e PRIMEIRO
self.clientes = ["Ana", "Bruno", "Carla"] # a lista é a fonte da verdade
self.lista = tk.Listbox(self, height=5) # Listbox = a list visual (aula 18)
self.lista.pack(padx=10, pady=10)
self.preencher()
# bind liga um evento a um callback — o command= generalizado
self.lista.bind("<<ListboxSelect>>", self.ao_selecionar)
tk.Button(self, text="Excluir", command=self.excluir).pack(pady=5)
def preencher(self):
self.lista.delete(0, tk.END) # limpa ANTES de reinserir (senão duplica)
for c in self.clientes:
self.lista.insert(tk.END, c) # o Listbox espelha a lista Python
def ao_selecionar(self, evento): # o handler recebe o evento
selecao = self.lista.curselection() # tupla de índices (vazia se nada)
if selecao:
print(self.clientes[selecao[0]])
def excluir(self):
selecao = self.lista.curselection()
if selecao and messagebox.askyesno("Excluir", "Confirma?"): # askyesno → bool
del self.clientes[selecao[0]]
self.preencher()
App().mainloop() # um único mainloop para toda a app Bloco 24 · SQLite
SQLite é um banco inteiro num arquivo .db, sem servidor. O ritual é o abrir → usar → fechar da aula 17. CREATE TABLE IF NOT EXISTS evita erro na 2ª execução.
import sqlite3
from pathlib import Path
BANCO = Path(__file__).parent / "loja.db" # o .db fica ao lado do script (aula 24)
conexao = sqlite3.connect(BANCO) # abre (cria o arquivo se não existe)
conexao.execute("""
CREATE TABLE IF NOT EXISTS produtos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nome TEXT NOT NULL,
preco REAL NOT NULL
)
""") # IF NOT EXISTS evita erro na 2ª execução
conexao.commit() # sem commit(), nada é gravado (erro nº 1)
conexao.close() # abrir → usar → fechar (aula 17)
print("Banco pronto") # Banco pronto Bloco 24 · SQLite
Valores de usuário SEMPRE por ? (nunca f-string — evita SQL injection). UPDATE/DELETE sem WHERE valem para TODAS as linhas: o WHERE é o cinto de segurança.
import sqlite3
conexao = sqlite3.connect("loja.db") # abre o arquivo do banco (rode num .db novo pra ver esta saída)
conexao.execute("CREATE TABLE produtos (id INTEGER PRIMARY KEY AUTOINCREMENT, nome TEXT, preco REAL)")
# INSERT com placeholder ? — NUNCA f-string (evita SQL injection, aula 24)
conexao.execute("INSERT INTO produtos (nome, preco) VALUES (?, ?)", ("Caneta azul", 2.5))
conexao.execute("INSERT INTO produtos (nome, preco) VALUES (?, ?)", ("Caneta preta", 2.5))
conexao.execute("INSERT INTO produtos (nome, preco) VALUES (?, ?)", ("Caderno", 15.0))
conexao.commit()
# SELECT com busca parcial: WHERE ... LIKE ?
cursor = conexao.execute("SELECT nome, preco FROM produtos WHERE nome LIKE ?", ("Caneta%",))
print(cursor.fetchall()) # [('Caneta azul', 2.5), ('Caneta preta', 2.5)]
# UPDATE com WHERE (o cinto de segurança — sem ele, altera TODAS as linhas)
conexao.execute("UPDATE produtos SET preco = ? WHERE id = ?", (18.0, 3))
print(conexao.execute("SELECT nome, preco FROM produtos WHERE id = ?", (3,)).fetchone())
# ('Caderno', 18.0)
conexao.close() Bloco 24 · SQLite
| item | detalhe |
|---|---|
| CREATE TABLE IF NOT EXISTS | Cria a tabela (uma vez). IF NOT EXISTS evita OperationalError na 2ª execução. |
| INSERT (Create) | INSERT INTO produtos (nome, preco) VALUES (?, ?). cursor.lastrowid devolve o id gerado. |
| SELECT (Read) | fetchall() → lista de tuplas; fetchone() → uma tupla ou None. WHERE ... LIKE ? filtra parcial. |
| UPDATE (Update) | UPDATE produtos SET preco = ? WHERE id = ?. Sempre com WHERE. |
| DELETE (Delete) | DELETE FROM produtos WHERE id = ?. Sem WHERE, apaga a tabela inteira. |
Bloco 24 · SQLite
from_row(linha) (@classmethod, tupla → objeto) e to_params() (objeto → tupla para os ?). É o parente do from_dict/to_dict da aula 17. salvar, listar, buscar_por_id, atualizar, remover). Encapsulamento (aula 04): quem chama nunca vê SQL. @classmethod: id, nome, preco = linha; return cls(nome, preco, id).
return (self.nome, self.preco) — SEM o id (o banco gera).
Erro nº 1: roda sem erro, mas os dados somem ao reabrir. Igual esquecer salvar_todos (aula 23).
A base sustenta tudo
A 2ª VA é cumulativa: os pilares das aulas 01–12 reaparecem dentro de cada tema novo. Reconhecer isso é meio caminho.
@property + @setter com raise ValueError protegem placa, preço, saldo. E o Repositório esconde o SQL.
App(tk.Tk) e TelaClientes(tk.Frame): a app e as telas herdam do Tkinter.
O Listbox mostra print(item) e cada objeto formata o próprio __str__ — sem if por tipo.
O contrato do Observer, do Strategy e do Factory Method: quem não implementa não instancia.
Frame compõe widgets (22); container compõe telas (23); o todo cria a parte no próprio __init__.
__str__ reaparece no Listbox e no from_row; o @classmethod factory vira from_dict (17) e from_row (24).
Integração
finally (14) → with (17): o with é o finally automático, que fecha o recurso sozinho. @classmethod factory (03) → from_dict (17) → from_row (24): o mesmo padrão de construtor alternativo, três vezes. sorted(key=lambda) (18) É Strategy (21); o command= de botão (22) É Observer (21). Padrão que você já usava com outro nome. .txt (17/23) → tabela SQLite (24): mesma ideia de "lembrar das coisas", banco mais robusto. objeto no objeto (05) → Frame compõe widgets (22) → container compõe telas (23).
super() (07) → App(tk.Tk) e TelaClientes(tk.Frame) (23).
@property (04) → o Repositório esconde o SQL (24).
Vocabulário
A prova cobra o termo técnico, não só o código. Saiba nomear cada um.
Sinalizar e tratar o erro. else = caminho feliz; finally = sempre.
Classe que herda de Exception e fala a língua do domínio.
Abre e fecha o arquivo sozinho. Sempre encoding="utf-8".
[expr for x in col if cond] — transformar e filtrar em uma linha.
Ordena por um critério. Devolve lista nova; .sort() devolve None.
O par que faz o set deduplicar objetos.
Gang of Four: o catálogo de 23 padrões clássicos de 1994.
Uma única instância; prova-se com is (identidade).
Quem cria / avisa muitos / delega a um algoritmo trocável.
A função como valor (sem parênteses): o Observer do Tkinter.
Lista visual / evento nomeado / diálogo (askyesno → bool).
App como classe; tkraise troca de tela sem abrir várias janelas.
SQL seguro; WHERE é o cinto de segurança do UPDATE/DELETE.
from_row/to_params; a classe que esconde o SQL.
Sem ele o INSERT roda, mas os dados somem ao reabrir.
A coluna que identifica cada linha; o banco gera (AUTOINCREMENT).
Armadilhas
Reconheceu o padrão, você corrige. Treine identificar cada uma antes de ler a resposta.
O bug some da tela, mas continua vivo. Capture específico e trate de verdade.
Para anexar sem perder o conteúdo, use "a". E write() não pula linha: coloque o \n.
.sort() ordena in-place e devolve None. Para guardar em variável, use sorted().
Para set vazio, use set(). E set de objetos exige __eq__ + __hash__.
O Acervo TEM itens (composição), não É um item — herdar da parte é o erro. Herança é só quando há relação 'é um'.
Executa na montagem e registra None: botão morto. Passe a função sem parênteses.
TclError: cannot use geometry manager grid inside . which already has slaves managed by pack. Um gerente por container.
Abre SQL injection. Valores de usuário SEMPRE por placeholder ?.
O INSERT roda sem erro, mas os dados somem ao reabrir. E UPDATE/DELETE sem WHERE atingem tudo.
Autoavaliação
Resumo em uma linha cada