Módulo 14 - Tratamento básico de erros

Prática: a calculadora à prova de erros

11 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Diagnosticar os pontos fracos da calculadora do módulo 6.
  • Construir a função pedir_numero com while True e try/except ValueError.
  • Tratar a divisão por zero com aviso amigável em vez de queda.
  • Montar o código final completo e testá-lo com entradas de má fé.

O plano da blindagem

Hora de cumprir a promessa feita lá no módulo 3: revisitar a calculadora com ferramentas novas. A versão do módulo 6 funcionava bem no cenário ideal, com um usuário educado digitando números certinhos. Só que programa de verdade não vive no cenário ideal. Faça o teste da má fé: rode a sua calculadora antiga e digite banana no primeiro número. Traceback com ValueError e programa no chão. Digite zero como divisor: ZeroDivisionError. Um programa que qualquer distração derruba não está pronto.

  1. Ponto fraco 1: float(input()) cai com ValueError se a entrada não for número.
  2. Ponto fraco 2: uma operação fora de +, -, * e / passa sem conferência.
  3. Ponto fraco 3: divisão por zero derruba tudo com ZeroDivisionError.

O plano de blindagem ataca os três pontos com o que você já tem no cinto de ferramentas: funções do módulo 13, while do módulo 8 e o try/except da aula passada. A peça central é um padrão que você vai reutilizar pelo resto da vida de programador: o loop de validação, que pede o dado, tenta usar, e repete a pergunta com uma mensagem amigável enquanto vier coisa inválida. O programa não briga com o usuário e não desiste dele; só insiste até dar certo.

A peça central: a função pedir_numero

Como a calculadora pede dois números, escrever a blindagem duas vezes seria repetição, e o módulo 13 ensinou o remédio para repetição: função. A pedir_numero abaixo junta while True com try/except num padrão de quatro linhas de lógica que resolve a leitura segura de números para sempre.

def pedir_numero(mensagem):
    while True:
        texto = input(mensagem)
        try:
            return float(texto)
        except ValueError:
            print(f"Ops: '{texto}' não é um número. Tente de novo.")

O loop de validação: tenta converter; se der ValueError, avisa e o while pergunta de novo.

Acompanhe o fluxo com calma, porque ele tem uma sutileza elegante. O while True parece o loop infinito que o módulo 8 mandou evitar, mas repare no return dentro do try: quando a conversão funciona, o return devolve o número E encerra a função na hora, loop incluído. A saída do loop é o sucesso. Quando a conversão falha, o except mostra a mensagem amigável, com o valor rejeitado entre aspas para o usuário ver o que digitou, e o while volta ao input. Resultado: a função só retorna número válido, não importa quantas tentativas custe.

Primeiro número: banana
Ops: 'banana' não é um número. Tente de novo.
Primeiro número: 12,5
Ops: '12,5' não é um número. Tente de novo.
Primeiro número: 12.5

A função em ação, insistindo com educação. Detalhe brasileiro: o Python usa ponto decimal, então 12,5 com vírgula também é rejeitado.

O código final completo

Agora a montagem final, atacando também os pontos fracos 2 e 3. A operação é validada com um while simples: enquanto o texto digitado não estiver entre as quatro aceitas, o programa explica e pede de novo. E a divisão por zero é resolvida ANTES da conta, com um if que detecta o caso e reabre a pergunta do divisor. É a validação com if e o try/except trabalhando juntos, cada um no que faz melhor, como combinamos na aula passada.

def pedir_numero(mensagem):
    while True:
        texto = input(mensagem)
        try:
            return float(texto)
        except ValueError:
            print(f"Ops: '{texto}' não é um número. Tente de novo.")

print("=== CALCULADORA À PROVA DE ERROS ===")

a = pedir_numero("Primeiro número: ")
b = pedir_numero("Segundo número: ")

operacao = input("Operação (+, -, * ou /): ")
while operacao not in ("+", "-", "*", "/"):
    print("Operação inválida. Use +, -, * ou /.")
    operacao = input("Operação (+, -, * ou /): ")

if operacao == "/" and b == 0:
    print("Divisão por zero não existe. Preciso de outro divisor.")
    while b == 0:
        b = pedir_numero("Segundo número (diferente de zero): ")

if operacao == "+":
    resultado = a + b
elif operacao == "-":
    resultado = a - b
elif operacao == "*":
    resultado = a * b
else:
    resultado = a / b

print(f"Resultado: {a} {operacao} {b} = {resultado}")

A calculadora blindada completa: números validados em loop, operação conferida e divisão por zero avisada.

=== CALCULADORA À PROVA DE ERROS ===
Primeiro número: dez
Ops: 'dez' não é um número. Tente de novo.
Primeiro número: 10
Segundo número: 0
Operação (+, -, * ou /): dividir
Operação inválida. Use +, -, * ou /.
Operação (+, -, * ou /): /
Divisão por zero não existe. Preciso de outro divisor.
Segundo número (diferente de zero): 4
Resultado: 10.0 / 4.0 = 2.5

O teste da má fé, agora sem vítimas: três entradas erradas seguidas e nenhum traceback.

Digite o código no Playground ou no VS Code e repita o teste da má fé do começo da aula: letra no número, operação inventada, zero no divisor. Compare a experiência com a versão do módulo 6 e sinta a diferença de qualidade. Essa é a marca de um programa robusto: ele não promete que o usuário nunca vai errar; ele promete reagir bem quando errar. Guarde a pedir_numero com carinho, porque o gerenciador de tarefas do projeto final vai precisar exatamente dela.

Teste rápido

Na função pedir_numero, o que acontece quando o usuário digita "abc"?

Perguntas frequentes

Por que usar while True se o curso ensinou a evitar loop infinito?
Porque este loop tem saída garantida e proposital: o return dispara na primeira conversão bem-sucedida e encerra função e loop juntos. Loop infinito de verdade é o que não tem caminho de saída; o while True com return no sucesso é um padrão clássico e seguro de validação.
Por que tratar a divisão por zero com if em vez de try/except?
As duas formas funcionam, e um except ZeroDivisionError seria perfeitamente válido. O if foi escolhido porque conferir b == 0 antes permite pedir outro divisor na hora, mantendo o fluxo da conversa. Regra prática: o que dá para prever, valide com if; o que só se descobre tentando, trate com try/except.
Por que a função usa float e não int?
Para a calculadora aceitar valores com casas decimais, como 12.5, que o int rejeitaria. O custo é o resultado sair como 10.0 em vez de 10. O desafio 2 propõe a versão pedir_inteiro exatamente para você sentir a diferença entre as duas conversões.
E se o usuário digitar espaços antes do número, tipo ' 10 '?
Funciona: o float do Python ignora espaços nas pontas do texto antes de converter. Já '1 0', com espaço no meio, é rejeitado com ValueError, e a sua blindagem responde pedindo de novo. Teste os dois casos no Playground para confirmar.
Posso copiar a pedir_numero para outros programas meus?
Deve. Foi desenhada para isso: recebe a mensagem como parâmetro, então serve para pedir idade, preço, nota ou qualquer número. Reaproveitar funções testadas em vez de reescrever é hábito profissional, e o projeto final do curso vai usar essa mesma função.
A calculadora blindada ainda tem alguma fraqueza?
Tem, e enxergar isso é sinal de maturidade: ela não aceita vírgula decimal à brasileira (desafio 3), faz uma conta só por execução (desafio 1) e insiste para sempre em vez de desistir com elegância (desafio 4). Robustez é escada, não interruptor: cada versão cobre mais casos que a anterior.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.