Unidade 6 · o tombo

O tombo

Quando algo é “excepcional” no programa, não é elogio: deu errado. SyntaxError você conserta. ValueError no teclado, arquivo que não existe no caderno — você antecipa.

Nesta unidade Primeiro o erro de digitação que impede o arquivo de rodar. Depois o teclado manda bolo onde se espera número — e o caderno da unidade 5 some do disco (FileNotFoundError). try / except, else, loop até cooperar, uma função get_int reutilizável.

Uma aspas e o programa nem começa

Arquivo recado.py, uma linha só, e você esquece de fechar as aspas:

print("Café na máquina.

Rode python recado.py. O traceback é maior que o programa. O que importa está embaixo: SyntaxError, unterminated string literal — você começou um texto e não terminou. Literal é o que você digitou entre aspas, não uma variável.

Erro de sintaxe é problema seu, na hora de escrever. Não tem except que pegue: o interpretador recusa o arquivo inteiro. Fecha a aspas, rode de novo, aparece o café.

O outro tipo: enquanto roda

Muita coisa só quebra depois que o programa já começou — em especial o que um humano digita. Você não adivinha o teclado. Por isso escreve código defensivo: detecta o problema e decide o que fazer, em vez de deixar o traceback na cara do caixa.

Arquivo novo, quantidade.py. Pergunta um inteiro, imprime o valor. Conversão com int, texto com f-string:

qtd = int(input("Quantos pães? "))
print(f"São {qtd} pães.")

50 funciona. Cantos que vale testar: 0, -1. Também funcionam — são inteiros. Agora o canto que não parece número: bolo.

Aí não é SyntaxError. Você não esqueceu gramática. É ValueError: invalid literal for int() with base 10: 'bolo'. Base 10 é o decimal de sempre. O int recusou o texto.

Imprimir “não digite bolo” não resolve: a pessoa não lê, ou tenta derrubar o programa de propósito. O caminho é tratar o erro.

6.A · bolo

int("bolo") sem try. O que acontece?

int só aceita texto que pareça inteiro.
Sem except, o interpretador desiste e mostra o erro.
Solução: ValueError, programa acaba.

Tentar, salvo se der errado

try é o nome certo: tente fazer isto. except ValueError — as maiúsculas importam — é o que roda se essa família de erro aparecer. SyntaxError, de novo, não entra aqui.

try:
    qtd = int(input("Quantos pães? "))
    print(f"São {qtd} pães.")
except ValueError:
    print("Não é um inteiro.")

Indentação: o que está sob o try é o que você tenta; o que está sob o except só roda no erro. 50 imprime a quantidade. bolo imprime o recado — sem traceback.

6.B · try

Um try com except (ValueError ou Exception) em volta de uma conversão.

try: na primeira linha, dois-pontos, bloco indentado.
except ValueError: e um print.
Modelo:
try:
n = int(input("n: "))
except ValueError:
print("Não é número.")

Não pegue o mundo inteiro

ValueError não é o único erro. Tem NameError, ZeroDivisionError, e uma lista que só cresce com os anos. A técnica é a mesma: nomeie o que você sabe tratar.

Dá para escrever except: sem tipo e engolir tudo. É preguiça. Esconde o bug que você queria ver no traceback. Melhor prática: descobrir que erro aquela função levanta e escrever o nome. A documentação oficial nem sempre avisa de antemão; vem com a prática.

6.C · específico

Por que evitar except: nu (sem nomear o erro)?

Um NameError seu também é uma exceção.
except ValueError deixa o resto aparecer.
Solução: except nu esconde bugs.

Tente o mínimo

O print da quantidade não levanta ValueError: f-string imprime int, str, float. Melhor prática: tente só a linha que pode quebrar — a conversão — e tire o print do try.

try:
    qtd = int(input("Quantos pães? "))
except ValueError:
    print("Não é um inteiro.")
print(f"São {qtd} pães.")

50 ainda funciona. bolo agora vira NameError: name 'qtd' is not defined, na linha do print.

Não é escopo de bloco (isso é C, C++, Java). Também não é variável “local ao try”. É ordem: o = copia da direita para a esquerda. input devolve string; int("bolo") explode antes de copiar. A esquerda nunca roda. qtd não existe. O print de baixo, alinhado à esquerda, executa de qualquer jeito.

6.D · NameError

Por que qtd “não está definido” se a linha do = está no arquivo?

O erro interrompe a atribuição no meio.
A gaveta da esquerda só recebe se a direita terminar.
Solução: a cópia nem aconteceu.

Se deu certo, então

O else do if você já viu. No try a intuição é a mesma: o resto, quando ninguém explodiu. Assim o print da quantidade só roda no sucesso; o except e o else se excluem.

try:
    qtd = int(input("Quantos pães? "))
except ValueError:
    print("Não é um inteiro.")
else:
    print(f"São {qtd} pães.")

50: tenta, copia, pula o except, entra no else. bolo: tenta, except, não entra no else — e qtd não é usado.

6.E · else

Qual a diferença entre else e finally no try?

Um dos dois é “sucesso”; o outro é “de qualquer jeito”.
finally não espera o except decidir. (O with do caderno, unidade 5, já usa essa ideia.)
Solução: else = sucesso; finally = sempre.

Perguntar de novo, não desistir

Mandar o usuário embora depois de um bolo é hostil. Na unidade 3 o loop já insistia até o número prestar. Aqui o mesmo: while True — pergunta verdadeira de propósito — e break quando a conversão der certo.

while True:
    try:
        qtd = int(input("Quantos pães? "))
    except ValueError:
        print("Não é um inteiro.")
    else:
        break

print(f"São {qtd} pães.")

O else ainda é do try, não do except. bolo, cão, ave: recado e pergunta de novo. 50: break, print fora do loop.

Se você meter o print dentro do loop e tirar o break, o programa nunca acaba: 50, 49, 48… Feature, se você quiser. Senão, precisa sair.

break vale em loop, em if, em try. Dá para pôr o break logo após a atribuição, ainda no try, e largar o else. Se int falhar, o fluxo pula para o except e o break nem roda. Os dois desenhos são corretos; o else deixa o try com uma linha só. Escolha o que você justifica, não o que “passou”.

Uma função que pede inteiro

Hoje e amanhã você vai querer inteiro do teclado. Invente get_int. O trabalho de perguntar some da vista do main — abstração, como na unidade 4.

def get_int():
    while True:
        try:
            qtd = int(input("Quantos pães? "))
        except ValueError:
            print("Não é um inteiro.")
        else:
            return qtd

def main():
    qtd = get_int()
    print(f"São {qtd} pães.")

main()

return é mais forte que break: sai do loop e da função, devolvendo o valor. Não precisa break e depois return na linha de baixo.

Ainda mais curto: se a variável só serve para devolver na hora, return int(input(...)) dentro do try. Menos linhas, menos lugar para errar; um pouco mais denso de ler. Os dois valem se você souber dizer por quê.

6.F · return

Uma função com return e conversão int(...) (o miolo de um get_int).

def get_int():
try: return int(input(...))
Modelo:
def get_int():
    while True:
        try:
            return int(input("Quantos? "))
        except ValueError:
            pass

Pegar o erro e não falar nada

“Não é um inteiro” três vezes cansa. Se quiser só perguntar de novo, capture o erro e pass — a palavra da unidade 2: sua vez de falar, e você passa. O traceback não aparece; o loop continua; o convite Quantos pães? se repete.

def get_int():
    while True:
        try:
            return int(input("Quantos pães? "))
        except ValueError:
            pass

Quem chamou get_int não fica sabendo que houve erro: o ponto de tratar é este. Recado explícito ou silêncio — os dois são mecanismos; escolha o que ajuda o humano na fila.

Indentação, de novo: o que está sob def é a função; sob while, o loop; sob try, a linha que tenta; sob except, o pass. Não é enfeite. É o que associa cada bloco a quem manda executar.

Dá para checar str.isdigit() (ou afins) antes do int. É razoável. O jeito desta linguagem costuma ser: tente, e trate se falhar — em vez de uma floresta de if na frente.

6.G · pass

No except ValueError: pass dentro do while True, o que o pass faz?

O except ainda pega o ValueError.
Sem break e sem return, o while volta à pergunta.
Solução: silêncio e nova pergunta.

Quem chama diz o convite

Hard-coded “Quantos pães?” prende a função a um programa só. O input nativo já recebe o texto do convite. Faça o mesmo: get_int ganha um parâmetro prompt. O main manda a frase; a função não precisa saber se a gaveta se chama qtd, y ou z.

def get_int(prompt):
    while True:
        try:
            return int(input(prompt))
        except ValueError:
            pass

def main():
    qtd = get_int("Quantos pães? ")
    print(f"São {qtd} pães.")

main()

Mesmo comportamento; código mais reutilizável. Quem usa a função (caller) não precisa coincidir o nome da variável com quem é chamado (callee).

Você também pode disparar o erro

raise ValueError("precisa ser positivo") recusa um valor seu — mesa com zero pessoas, quantidade negativa. Quem chamou trata ou o programa cai, honesto. Não é o mesmo que sys.exit (unidade 7): exit encerra o script; raise é para o except do colega. Nesta unidade o miolo é capturar; disparar aparece de novo quando a função valida de propósito.

6.H · raise

Uma linha (ou bloco) com raise e um erro nomeado.

raise ValueError("mensagem")
Pode ir dentro de um if.
Exemplo: raise ValueError("precisa ser positivo")

Oficina · get_int

Problema da unidade Função que pede um inteiro e não cai no traceback se a pessoa digitar lixo. Loop até cooperar. try / except ValueError. Pode imprimir recado ou só pass. Opcional: parâmetro prompt. Arquivo: quantidade.py.
Oficina 6

Cole o programa: input + try/except (função vale).

while True por fora, try com int(input) por dentro.
except ValueError: pass ou print. Sucesso: return ou break.
Modelo:
def get_int(prompt):
    while True:
        try:
            return int(input(prompt))
        except ValueError:
            pass

n = get_int("Quantos pães? ")
print(n)