Python try e except

Na aula Lendo uma Mensagem de Erro, todo erro terminava do mesmo jeito: o Python mostrava o traceback e parava o programa. Muitas vezes é isso mesmo que você quer. Mas tem erro que você sabe que pode acontecer: a pessoa vai digitar "trinta" em vez de 30, vai dividir a conta por zero pessoas. Para esses casos, o Python tem o try (em inglês, "tente") e o except ("exceto", "a não ser que"): tente fazer isto; se der tal erro, faça aquilo, e siga em frente.

Você vai aprender
  • A escrever um try com except e o caminho que o Python faz quando dá erro e quando não dá.
  • Por que dizer qual erro tratar (except ValueError) e por que evitar o except: sozinho, que engole até o Ctrl+C.
  • A ler a mensagem do erro com as e.
  • A tratar erros diferentes de jeitos diferentes, com vários except.
  • A pedir um número de novo, quantas vezes for preciso, até a pessoa acertar.
  • Os erros reais de quem está aprendendo try.

Todos os programas desta aula rodaram no Python 3.14 da máquina da Ana (usuária ana, computador notebook), salvos na pasta pessoal dela e executados com python3 arquivo.py. O que aparece depois de cada pergunta (Sua idade: 30) é o que a Ana digitou.


A ideia: o plano B da receita

A Ana vai fazer um bolo. A receita diz: "bata 3 ovos". Ela abre a geladeira e… tem ovo? Na maioria das vezes, tem, e ela segue a receita. Mas ela já combinou consigo mesma um plano B: "se não tiver ovo, faço o bolo de banana, que não leva". O plano B fica guardado e só é usado se o problema acontecer.

É assim que o try funciona:

  • no try vai o plano A, o que você quer fazer (e que pode dar errado);
  • no except vai o plano B, para um problema específico ("se não tiver ovo");
  • depois dos dois, a vida segue: o programa continua na linha de baixo, nos dois casos.

Repare que o plano B da Ana é para um problema: falta de ovo. Se o forno quebrar, o bolo de banana não resolve. Guarde essa ideia; ela é a seção 3 inteira.


1. O primeiro try

Um programa sem try, que pergunta a idade:

idade = int(input("Sua idade: "))
print(f"Ano que vem você terá {idade + 1} anos.")
print("Obrigado!")

Com um número, tudo bem. Com uma palavra, o int() não consegue converter, e o programa para com um ValueError (o "erro de valor" da aula Lendo uma Mensagem de Erro):

$ python3 idade_sem.py
Sua idade: 30
Ano que vem você terá 31 anos.
Obrigado!
$ python3 idade_sem.py
Sua idade: trinta
Traceback (most recent call last):
  File "/home/ana/idade_sem.py", line 1, in <module>
    idade = int(input("Sua idade: "))
ValueError: invalid literal for int() with base 10: 'trinta'
$ echo $?
1

Nem o Obrigado! apareceu. Agora com try e except:

try:
    idade = int(input("Sua idade: "))
    print(f"Ano que vem você terá {idade + 1} anos.")
except ValueError:
    print("Isso não é um número inteiro.")
print("Obrigado!")
try:
"Tente". Começa o bloco do plano A. Os dois-pontos e a indentação funcionam como no if: as linhas de dentro ficam 4 espaços para a direita.
idade = int(input(…))
A linha que pode dar erro: a conversão do que a pessoa digitou.
print(f"Ano que vem…")
Também está dentro do try: só faz sentido se a conversão deu certo.
except ValueError:
"A não ser que dê ValueError". Fica na mesma coluna do try. O nome depois do except é o tipo de erro que este bloco trata, exatamente como aparece na última linha do traceback.
print("Isso não é…")
O plano B. Só roda se, dentro do try, acontecer um ValueError.
print("Obrigado!")
Fora dos dois blocos (na margem esquerda). Roda sempre, depois de tudo.
$ python3 idade_try.py
Sua idade: 30
Ano que vem você terá 31 anos.
Obrigado!
$ python3 idade_try.py
Sua idade: trinta
Isso não é um número inteiro.
Obrigado!

Com 30, o programa fez exatamente o mesmo que antes: o except nem foi usado. Com trinta, em vez do traceback veio a mensagem do plano B, e o programa continuou até o Obrigado!. Quando um erro é pego por um except, os programadores dizem que ele foi tratado (ou "capturado").

Missão: a caixa vai "digitar" trinta. Rode uma vez para ver o ValueError. Depois coloque as duas primeiras linhas dentro de um try e acrescente um except ValueError: que mostre Isso não é um número inteiro., para o programa chegar até o Obrigado!.

idade = int(input("Sua idade: "))
print(f"Ano que vem você terá {idade + 1} anos.")
print("Obrigado!")
try:
    idade = int(input("Sua idade: "))
    print(f"Ano que vem você terá {idade + 1} anos.")
except ValueError:
    print("Isso não é um número inteiro.")
print("Obrigado!")

2. O caminho do Python dentro do try

Para ver exatamente o que roda e o que é pulado, um programa que só anuncia cada passo:

print("1. Antes do try")
try:
    print("2. Começou o try")
    numero = int("dez")
    print("3. Depois da conversão")
except ValueError:
    print("4. Dentro do except")
print("5. Depois de tudo")
$ python3 fluxo.py
1. Antes do try
2. Começou o try
4. Dentro do except
5. Depois de tudo

O passo 3 sumiu. Quando o erro acontece numa linha do try, o Python abandona o resto do try na hora e pula para o except. Agora com int("10"), que dá certo:

print("1. Antes do try")
try:
    print("2. Começou o try")
    numero = int("10")
    print("3. Depois da conversão")
except ValueError:
    print("4. Dentro do except")
print("5. Depois de tudo")
$ python3 fluxo_ok.py
1. Antes do try
2. Começou o try
3. Depois da conversão
5. Depois de tudo

Sem erro, o try roda inteiro e o except é pulado: o passo 4 não aparece.

Sem erroCom ValueError na linha do meio
Linhas do try antes do erroRodamRodam
A linha que deu erro—Para no meio
Linhas do try depois do erroRodamNão rodam
O bloco exceptNão rodaRoda
Depois do try/exceptRodaRoda

3. Diga qual erro você está esperando

O except ValueError trata só o ValueError. Se dentro do try acontecer outro erro, ele passa direto, como se o try nem existisse:

try:
    total = 120
    pessoas = 0
    print(total / pessoas)
except ValueError:
    print("Valor inválido.")
print("Fim")
$ python3 tipo_errado.py
Traceback (most recent call last):
  File "/home/ana/tipo_errado.py", line 4, in <module>
    print(total / pessoas)
          ~~~~~~^~~~~~~~~
ZeroDivisionError: division by zero

O erro foi um ZeroDivisionError, e o plano B era para ValueError. É o forno quebrado da receita: o bolo de banana não resolve. Isso não é um defeito do Python, é uma qualidade: o except trata só o problema que você previu e sabe resolver, e os outros continuam aparecendo, para você ver.

O except: sozinho (o "except nu")

O Python aceita um except sem nenhum tipo: except:. Ele pega qualquer erro. Parece prático, mas esconde problemas. Olhe este programa, com um nome digitado errado (txto em vez de texto):

texto = input("Quantidade: ")
try:
    quantidade = int(txto)
    print(f"Anotado: {quantidade}")
except:
    print("Isso não é um número!")
$ python3 nu_nome.py
Quantidade: 7
Isso não é um número!

A Ana digitou 7, um número perfeito, e o programa disse que não era. O erro de verdade foi um NameError (txto não existe), mas o except: pegou esse erro também e mostrou a mensagem errada. Sem o traceback, a Ana não tem a menor pista: o Did you mean: 'texto'? que resolveria tudo nunca apareceu.

E piora. O Ctrl+C, que serve para interromper um programa (veja while), também chega ao Python como uma exceção, o KeyboardInterrupt. O except: pega até ele:

while True:
    try:
        idade = int(input("Sua idade: "))
        break
    except:
        print("Isso não é um número. Tente de novo.")
print(f"Idade registrada: {idade}")
$ python3 nu_ctrlc.py
Sua idade: ^C                                       « a Ana apertou Ctrl+C para sair…
Isso não é um número. Tente de novo.                « …e o programa não deixou
Sua idade: ^C
Isso não é um número. Tente de novo.
Sua idade: 30
Idade registrada: 30

A Ana apertou Ctrl+C duas vezes e o programa não parou: só saiu quando ela digitou um número. (Esse laço que pede de novo é a seção 6.)

Do mais específico ao mais genérico

FormaPegaQuando usar
except ValueError:Só o ValueErrorQuase sempre. Você diz qual problema previu
except (ValueError, ZeroDivisionError):Os tipos da listaQuando vários erros têm o mesmo plano B (seção 5)
except Exception:Quase todos os erros (mas não o Ctrl+C)Raramente: na "última linha de defesa" de um programa grande, e mostrando qual foi o erro
except:Tudo, até Ctrl+CEvite. Esconde erros de digitação e não deixa interromper o programa

O except Exception pega quase tudo, mas deixa passar o Ctrl+C. O mesmo laço de cima, com except Exception: no lugar de except::

$ python3 exc_ctrlc.py
Sua idade: ^C
Traceback (most recent call last):
  File "/home/ana/exc_ctrlc.py", line 3, in <module>
    idade = int(input("Sua idade: "))
                ~~~~~^^^^^^^^^^^^^^^
KeyboardInterrupt

Ainda assim, ele esconderia o NameError do txto. Se um dia precisar dele, pelo menos mostre qual foi o erro, como na próxima seção.


4. as e: a mensagem do erro

Às vezes você quer tratar o erro e saber o que o Python disse. Escreva as ("como") e um nome depois do tipo; o erro fica guardado nessa variável dentro do except:

texto = "12,5"
try:
    quantidade = int(texto)
except ValueError as e:
    print("Não deu para converter.")
    print(f"Mensagem do Python: {e}")
    print(f"Tipo do erro: {type(e).__name__}")
$ python3 as_e.py
Não deu para converter.
Mensagem do Python: invalid literal for int() with base 10: '12,5'
Tipo do erro: ValueError
as e
Guarda o erro na variável e (de "erro"; também se vê err ou erro). Ela só existe dentro do bloco except.
{e}
Numa f-string (ou num print(e)), o erro vira a mensagem: o que vem depois dos dois-pontos na última linha do traceback.
type(e).__name__
O nome do tipo do erro, como texto (ValueError). O type() você conhece da aula Descobrindo o Tipo; o __name__ (com dois sublinhados de cada lado) é o nome dele.

Com except Exception as e, aquele programa do txto pelo menos conta o que aconteceu:

texto = input("Quantidade: ")
try:
    quantidade = int(txto)
    print(f"Anotado: {quantidade}")
except Exception as e:
    print(f"Deu erro: {type(e).__name__}: {e}")
$ python3 exception.py
Quantidade: 7
Deu erro: NameError: name 'txto' is not defined

Ainda é pior que o traceback (sumiram a linha e a dica Did you mean), mas já dá uma pista. O melhor continua sendo except ValueError: aí o NameError aparece completo.


5. Vários except

A Ana e os amigos vão dividir a conta de R$ 120 da pizzaria. Duas coisas podem dar errado: alguém digitar a quantidade por extenso (ValueError) ou digitar 0 (ZeroDivisionError). Cada problema merece uma resposta diferente, então cada um ganha o seu except:

total = 120.0
try:
    pessoas = int(input("Quantas pessoas vão dividir a conta? "))
    parte = total / pessoas
    print(f"Cada um paga R$ {parte:.2f}")
except ValueError:
    print("Digite a quantidade em algarismos, como 4.")
except ZeroDivisionError:
    print("Ninguém para dividir? Então a conta é toda sua!")
$ python3 conta.py
Quantas pessoas vão dividir a conta? 4
Cada um paga R$ 30.00
$ python3 conta.py
Quantas pessoas vão dividir a conta? quatro
Digite a quantidade em algarismos, como 4.
$ python3 conta.py
Quantas pessoas vão dividir a conta? 0
Ninguém para dividir? Então a conta é toda sua!

O Python olha os except de cima para baixo e usa o primeiro cujo tipo combina com o erro. Só um deles roda.

Um plano B para vários erros

Quando o plano B é o mesmo, junte os tipos entre parênteses (uma tupla):

total = 120.0
try:
    pessoas = int(input("Quantas pessoas vão dividir a conta? "))
    parte = total / pessoas
    print(f"Cada um paga R$ {parte:.2f}")
except (ValueError, ZeroDivisionError) as e:
    print(f"Não deu para dividir: {e}")
$ python3 conta_tupla.py
Quantas pessoas vão dividir a conta? 0
Não deu para dividir: division by zero
$ python3 conta_tupla.py
Quantas pessoas vão dividir a conta? 4,5
Não deu para dividir: invalid literal for int() with base 10: '4,5'

Missão: a caixa vai "digitar" 0. O programa já trata o ValueError, mas não o ZeroDivisionError. Acrescente um segundo except que mostre Ninguém para dividir? Então a conta é toda sua!.

total = 120.0
try:
    pessoas = int(input("Quantas pessoas vão dividir a conta? "))
    parte = total / pessoas
    print(f"Cada um paga R$ {parte:.2f}")
except ValueError:
    print("Digite a quantidade em algarismos, como 4.")
total = 120.0
try:
    pessoas = int(input("Quantas pessoas vão dividir a conta? "))
    parte = total / pessoas
    print(f"Cada um paga R$ {parte:.2f}")
except ValueError:
    print("Digite a quantidade em algarismos, como 4.")
except ZeroDivisionError:
    print("Ninguém para dividir? Então a conta é toda sua!")

Mais um exemplo, com a lista de compras: o IndexError (posição que não existe) também se trata assim.

compras = ["arroz", "feijão", "café"]
try:
    posicao = int(input("Qual item (0, 1, 2...)? "))
    print(f"Item: {compras[posicao]}")
except ValueError:
    print("Digite um número.")
except IndexError:
    print(f"A lista só tem {len(compras)} itens.")
$ python3 lista_ex.py
Qual item (0, 1, 2...)? 1
Item: feijão
$ python3 lista_ex.py
Qual item (0, 1, 2...)? 5
A lista só tem 3 itens.

6. Pedir de novo até a pessoa acertar

Até aqui, quando a pessoa errava, o programa avisava e terminava. Muito melhor é perguntar de novo. Junte o try com um while True (o laço "para sempre" da aula while) e um break (veja break e continue):

while True:
    try:
        idade = int(input("Sua idade: "))
        break
    except ValueError:
        print("Isso não é um número. Tente de novo.")
print(f"Idade registrada: {idade}")
while True:
Repete para sempre… até um break.
idade = int(input(…))
Tenta ler e converter.
break
Só chega aqui se a conversão deu certo (se der erro, o resto do try é pulado, lembra da seção 2?). O break sai do laço.
except ValueError:
Se a conversão falhou: avisa. Depois do except acaba a volta, e o while pergunta de novo.
print(f"Idade registrada…")
Fora do laço: só roda depois do break, com uma idade que com certeza é um número.
$ python3 laco.py
Sua idade: dez
Isso não é um número. Tente de novo.
Sua idade: vinte e cinco
Isso não é um número. Tente de novo.
Sua idade: 25
Idade registrada: 25

E como este laço usa except ValueError, e não except:, o Ctrl+C funciona:

$ python3 laco.py
Sua idade: ^C
Traceback (most recent call last):
  File "/home/ana/laco.py", line 3, in <module>
    idade = int(input("Sua idade: "))
                ~~~~~^^^^^^^^^^^^^^^
KeyboardInterrupt

$ echo $?
130

O traceback do KeyboardInterrupt aqui é bem-vindo: é a Ana mandando parar, e o programa obedecendo. O código de saída 130 é o de quem foi interrompido com Ctrl+C.


Formas diferentes de fazer a mesma coisa

Para não deixar o int() quebrar, existem duas filosofias. A do try: tente, e trate se der errado. E a do if: confira antes, e só faça se estiver tudo certo. O método isdigit() diz se um texto só tem algarismos (veja Métodos de Texto):

texto = input("Sua idade: ")
if texto.isdigit():
    idade = int(texto)
    print(f"Ano que vem você terá {idade + 1} anos.")
else:
    print("Isso não é um número inteiro.")
$ python3 lbyl.py
Sua idade: 30
Ano que vem você terá 31 anos.
$ python3 lbyl.py
Sua idade: -5
Isso não é um número inteiro.

Funciona, mas repare no segundo caso: -5 é um número inteiro, e o isdigit() recusou, porque o sinal de menos não é um algarismo. Conferir antes obriga você a prever todos os formatos válidos; o try deixa o próprio int() decidir, e ele aceita -5, +5 e 5 com espaços. Para dicionários, a conferência tem um atalho pronto, o get() (veja Dicionários):

precos = {"pão": 0.75, "leite": 5.49}
try:
    print(precos["café"])
except KeyError:
    print("Café sem preço cadastrado.")
print(precos.get("café", "sem preço"))
$ python3 get.py
Café sem preço cadastrado.
sem preço
SituaçãoQual usar
Converter o que a pessoa digitou (int(), float())try/except ValueError: o int() já sabe todas as regras
Abrir um arquivo que pode não existirtry/except FileNotFoundError (aula Abrindo Arquivos)
Uma chave que pode faltar no dicionáriodicionario.get(chave, padrão); ou if chave in dicionario
Uma condição simples e fácil de testar (lista vazia, divisor zero)if: fica mais claro de ler
Um erro que você não sabe consertarNenhum dos dois: deixe o traceback aparecer

Quando dá errado

try sem except
try:
    idade = int(input("Sua idade: "))
print("Obrigado!")
$ python3 sem_except.py
  File "/home/ana/sem_except.py", line 3
    print("Obrigado!")
    ^^^^^
SyntaxError: expected 'except' or 'finally' block

"Esperava um bloco except ou finally." Um try não pode ficar sozinho: logo depois do bloco dele tem de vir pelo menos um except (ou um finally, da próxima aula, else e finally). Como é erro de sintaxe, nada rodou, nem a pergunta. Como consertar: acrescente o except com o tipo de erro esperado.

Faltam os dois-pontos
try:
    idade = int(input("Sua idade: "))
except ValueError
    print("Isso não é um número.")
$ python3 sem_doispontos.py
  File "/home/ana/sem_doispontos.py", line 3
    except ValueError
                     ^
SyntaxError: expected ':'

"Esperava :." O ^ aponta o fim da linha, onde os dois-pontos deveriam estar. try: e except …: sempre terminam com dois-pontos.

Um erro dentro do próprio except
try:
    idade = int("trinta")
except ValueError as e:
    print("Erro: " + e)
$ python3 durante.py
Traceback (most recent call last):
  File "/home/ana/durante.py", line 2, in <module>
    idade = int("trinta")
ValueError: invalid literal for int() with base 10: 'trinta'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/ana/durante.py", line 4, in <module>
    print("Erro: " + e)
          ~~~~~~~~~^~~
TypeError: can only concatenate str (not "ValueError") to str

Dois tracebacks! A frase do meio diz: "Durante o tratamento da exceção acima, outra exceção aconteceu." O primeiro (ValueError) foi tratado direitinho; o problema é o segundo, que estourou dentro do except, na linha 4: e é um erro, não um texto, e o + não junta texto com erro. Leia o de baixo primeiro, como sempre. Como consertar: use uma f-string, print(f"Erro: {e}"), que converte o erro em texto sozinha.

O genérico antes do específico
try:
    pessoas = int("zero")
except Exception:
    print("Algum erro aconteceu.")
except ValueError:
    print("Digite em algarismos.")
$ python3 ordem.py
Algum erro aconteceu.

Nenhuma mensagem de erro, mas o resultado está errado: a mensagem útil (Digite em algarismos.) nunca aparece. O Python usa o primeiro except que combina, e o Exception combina com o ValueError (e com quase tudo). Como consertar: os except mais específicos vêm primeiro; o genérico, se houver, por último.

Um try grande demais

Quanto mais linhas dentro do try, maior a chance de o except pegar um erro que não era o previsto. Coloque no try só a linha que pode dar o erro esperado e o que depende dela, e deixe o resto de fora. A ferramenta para separar "o que só roda se deu certo" é o else, da próxima aula: else e finally.

except: escondendo um erro de digitação

Você viu na seção 3: com except: (ou except Exception: sem mostrar o erro), um NameError vira "Isso não é um número!" e você perde o traceback. Se o seu programa diz que algo deu errado e você não entende por quê, procure um except genérico e troque pelo tipo certo: o traceback de verdade vai aparecer.


Referência rápida

EscritaO que faz
try:Começa o bloco que pode dar erro
except ValueError:Roda se acontecer um ValueError no try
except ValueError as e:Idem, guardando o erro em e (print(e) mostra a mensagem)
except (ValueError, KeyError):Roda para qualquer um dos tipos da tupla
vários except seguidosO primeiro que combina é usado; só um roda
except Exception as e:Quase todos os erros; use pouco e mostre e
except:Tudo, até Ctrl+C: evite
type(e).__name__O nome do tipo do erro, como texto
while True: + try + breakPergunta de novo até dar certo
SintomaCausa provável
SyntaxError: expected 'except' or 'finally' blocktry sem except (ou com o except indentado errado)
SyntaxError: expected ':'Faltaram os dois-pontos do try ou do except
O traceback aparece mesmo com tryO erro foi de outro tipo, e não do que está no except
During handling of the above exception…Deu erro dentro do bloco except
A mensagem do plano B aparece sem motivoexcept: ou except Exception: pegando um erro não previsto
Ctrl+C não para o programaexcept: sozinho, dentro de um laço

Exercícios

1. Complete o tratamento

Complete para tratar o erro de um número mal digitado:

: preco = float(input("Preço: ")) : print("Use ponto nos centavos, como 4.50")

2. O que aparece?

Quantas linhas este programa mostra?

try:
    print("A")
    x = int("oi")
    print("B")
except ValueError:
    print("C")
print("D")
linhas

3. Dentro de um try com except ValueError: acontece um ZeroDivisionError. O que acontece?

4. Por que evitar o except: sem tipo?

5. Em except ValueError as e:, o que mostra print(e)?

Missão final: insistir até vir um número

Missão: a caixa vai "digitar" dez, vinte e 18. Do jeito que está, o programa quebra logo no dez. Coloque a leitura e o break dentro de um try, com um except ValueError: que mostre Isso não é um número. Tente de novo., para o programa insistir até chegar ao 18.

while True:
    idade = int(input("Sua idade: "))
    break
print(f"Idade registrada: {idade}")
while True:
    try:
        idade = int(input("Sua idade: "))
        break
    except ValueError:
        print("Isso não é um número. Tente de novo.")
print(f"Idade registrada: {idade}")

Resumo

  • try: guarda o código que pode dar erro; except Tipo: é o plano B para aquele tipo de erro. Depois dos dois, o programa continua.
  • Se o erro acontece no meio do try, o resto do try é pulado e o except roda. Sem erro, o except é pulado.
  • Diga o tipo: except ValueError. Erros de outros tipos continuam aparecendo, o que é bom.
  • Evite except: sozinho: ele esconde erros de digitação e engole o Ctrl+C. except Exception é o máximo de genérico aceitável, e mostrando o erro.
  • as e guarda o erro: print(f"{e}") mostra a mensagem, type(e).__name__ o tipo.
  • Vários except: o primeiro que combina é usado; do mais específico ao mais genérico. Vários tipos com o mesmo tratamento: except (A, B):.
  • while True + try + break pergunta de novo até a pessoa acertar.