Módulo 9 - Erros e exceções: nível avançado
Prática: validação robusta de um saque
12 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Modelar as regras de um saque com exceções próprias bem nomeadas.
- Validar valor e saldo antes de alterar a conta, falhando cedo.
- Tratar cada exceção no programa que chama, com mensagem específica.
- Reunir raise, except, else e finally num exemplo completo.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Prática: validação robusta de um saque.
Os objetivos desta aula. Modelar as regras de um saque com exceções próprias bem nomeadas. Validar valor e saldo antes de alterar a conta, falhando cedo. Tratar cada exceção no programa que chama, com mensagem específica. Reunir raise, except, else e finally num exemplo completo.
Veja o essencial, parte por parte.
O plano da prática. Crie uma família de exceções: ErroDaConta como base, filhas específicas herdando dela.
A função de saque, validando cedo. A função valida na ordem e falha cedo: primeiro checa se o valor é um número positivo, depois se está dentro do limite por saque, por fim se há saldo.
Tratando cada caso no programa. Adicione um depositar com as suas próprias validações e exceções.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
O plano da prática
Vamos construir uma função sacar que se comporta como um caixa eletrônico honesto: ela recusa operações inválidas com clareza, em vez de devolver resultados errados. As regras são três. O valor do saque precisa ser um número positivo. O valor não pode passar do saldo. E, para deixar realista, existe um limite por saque. Cada regra violada levanta uma exceção própria, com nome que conta a história. Assim, quem usar a função saberá exatamente o que deu errado e poderá tratar cada situação do jeito certo.
Vamos organizar as exceções em família. Uma classe base, ErroDaConta, herda de Exception. As específicas herdam de ErroDaConta: ValorInvalidoError para valores que não fazem sentido, SaldoInsuficienteError para saque acima do saldo e LimiteExcedidoError para saque acima do teto por operação. Essa hierarquia dá flexibilidade: quem chama pode tratar cada erro individualmente ou capturar ErroDaConta para pegar qualquer problema da conta de uma vez só. É o mesmo padrão usado em bibliotecas de verdade.
class ErroDaConta(Exception):
"""Classe base para os erros de operação da conta."""
class ValorInvalidoError(ErroDaConta):
"""Valor de saque que não é um número positivo."""
class SaldoInsuficienteError(ErroDaConta):
"""Saque maior que o saldo disponível."""
class LimiteExcedidoError(ErroDaConta):
"""Saque acima do limite permitido por operação."""Uma base e três filhas: cada erro da conta ganha um nome próprio dentro da família.
A função de saque, validando cedo
A função valida na ordem e falha cedo: primeiro checa se o valor é um número positivo, depois se está dentro do limite por saque, por fim se há saldo. Só quando todas as regras passam ela calcula o novo saldo e o devolve. Repare que nada é alterado antes das validações; a função não mexe no saldo para depois descobrir que o valor era inválido. Isso mantém a invariante do sistema: o saldo nunca fica negativo nem é corrompido por uma entrada ruim. Cada raise carrega uma mensagem com os números envolvidos.
LIMITE_POR_SAQUE = 1000
def sacar(saldo, valor):
if not isinstance(valor, (int, float)) or valor <= 0:
raise ValorInvalidoError(f"O valor do saque deve ser positivo: {valor!r}")
if valor > LIMITE_POR_SAQUE:
raise LimiteExcedidoError(
f"Saque de {valor} acima do limite de {LIMITE_POR_SAQUE} por operação"
)
if valor > saldo:
raise SaldoInsuficienteError(
f"Saque de {valor} maior que o saldo de {saldo}"
)
return saldo - valor
print(sacar(500, 200)) # 300
print(sacar(500, 800)) # levanta SaldoInsuficienteErrorAs três regras são checadas antes de alterar o saldo; cada falha levanta a exceção certa.
Vale um cuidado com a ordem das checagens. Colocamos o limite por saque antes do saldo de propósito: um pedido de 5000 numa conta de 500 estoura tanto o limite quanto o saldo, e faz mais sentido avisar primeiro sobre o teto da operação. Ordenar as validações da mais fundamental para a mais específica evita mensagens confusas. Essa é uma decisão de projeto pequena, mas é o tipo de detalhe que diferencia uma função robusta de uma que só funciona nos casos fáceis.
Tratando cada caso no programa
Com a função pronta, o programa que a usa fica limpo e expressivo. Ele tenta o saque dentro de um try e trata cada exceção com um except específico, dando a mensagem adequada a cada situação: valor inválido, limite excedido, saldo insuficiente. O bloco else roda só quando o saque dá certo, mostrando o novo saldo, e o finally registra que a operação foi concluída, tenha dado certo ou não. É o encontro de tudo que o módulo ensinou: exceções próprias organizadas em família, raise na função, e except, else e finally no tratamento.
def operar(saldo, valor):
try:
novo_saldo = sacar(saldo, valor)
except ValorInvalidoError as erro:
print("Valor inválido:", erro)
except LimiteExcedidoError as erro:
print("Limite excedido:", erro)
except SaldoInsuficienteError as erro:
print("Saldo insuficiente:", erro)
else:
print("Saque aprovado. Novo saldo:", novo_saldo)
return novo_saldo
finally:
print("Operação registrada no extrato.")
return saldo
saldo = 500
saldo = operar(saldo, 200) # aprovado, saldo 300
saldo = operar(saldo, 5000) # limite excedido
saldo = operar(saldo, -50) # valor inválido
saldo = operar(saldo, 900) # saldo insuficienteCada except trata um erro da família; o else mostra o sucesso e o finally sempre registra.
Ao rodar, você vê cada caminho: o primeiro saque é aprovado e o saldo cai para 300; o pedido de 5000 bate no limite; o valor negativo é recusado como inválido; o saque de 900 não passa porque o saldo agora é 300. Em todas as chamadas, a linha do finally aparece, porque a operação sempre precisa ser registrada no extrato. Como as filhas herdam de ErroDaConta, você poderia trocar os três except específicos por um único except ErroDaConta quando o tratamento fosse o mesmo para todos. Ter as duas opções é justamente a vantagem de organizar as exceções em família.
Teste rápido
Por que a função sacar valida o valor antes de calcular o novo saldo?
Perguntas frequentes
- Por que criar uma classe base ErroDaConta?
- Ela agrupa os erros da conta numa família. Assim, quem chama pode tratar cada filho de forma específica com vários except, ou capturar tudo de uma vez com except ErroDaConta quando o tratamento for igual. Essa flexibilidade é o padrão usado em bibliotecas reais.
- A ordem das validações na função importa?
- Importa para a clareza da mensagem. Um pedido pode violar mais de uma regra ao mesmo tempo. Checar da regra mais fundamental para a mais específica, como limite antes de saldo, faz a função avisar sobre o problema mais relevante primeiro, evitando mensagens confusas.
- Por que não devolver None quando o saque falha?
- Porque um None obriga quem chama a lembrar de checar, e um esquecimento vira bug silencioso. Uma exceção própria não passa despercebida: ou é tratada com except, ou aparece no traceback. E ela carrega uma mensagem explicando exatamente qual regra foi violada.
- Para que serve o else nesse programa?
- O else roda só quando o saque dá certo, ou seja, quando o try passou sem exceção. É onde mostramos o novo saldo e o devolvemos. Separar isso do try deixa claro que essa parte pertence ao caminho de sucesso, e não à tentativa que pode falhar.
- Posso rodar essa prática inteira no Playground do curso?
- Pode. O código usa apenas a linguagem e uma constante, sem pacotes externos, então roda no Playground sem instalar nada. Experimente remover um dos except e chamar aquele caso para ver o traceback: é uma ótima forma de entender o que cada bloco protege.
- Como eu estenderia isso para uma conta de verdade?
- O próximo passo natural é transformar a conta numa classe, tema dos módulos de orientação a objetos, com saldo guardado no objeto e métodos sacar e depositar. As exceções próprias continuam iguais; muda só onde o saldo vive. A base de validação que você montou aqui se mantém.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.