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.
- A escrever um
trycomexcepte 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 oexcept: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
tryvai o plano A, o que você quer fazer (e que pode dar errado); - no
exceptvai 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 dotry. O nome depois doexcepté 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 umValueError. - 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 erro | Com ValueError na linha do meio | |
|---|---|---|
Linhas do try antes do erro | Rodam | Rodam |
| A linha que deu erro | — | Para no meio |
Linhas do try depois do erro | Rodam | Não rodam |
O bloco except | Não roda | Roda |
Depois do try/except | Roda | Roda |
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
| Forma | Pega | Quando usar |
|---|---|---|
except ValueError: | Só o ValueError | Quase sempre. Você diz qual problema previu |
except (ValueError, ZeroDivisionError): | Os tipos da lista | Quando 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+C | Evite. 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êerrouerro). Ela só existe dentro do blocoexcept. - {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). Otype()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?). Obreaksai do laço. - except ValueError:
- Se a conversão falhou: avisa. Depois do
exceptacaba a volta, e owhilepergunta 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ção | Qual 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 existir | try/except FileNotFoundError (aula Abrindo Arquivos) |
| Uma chave que pode faltar no dicionário | dicionario.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 consertar | Nenhum 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.
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.
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.
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.
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
| Escrita | O 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 seguidos | O 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 + break | Pergunta de novo até dar certo |
| Sintoma | Causa provável |
|---|---|
SyntaxError: expected 'except' or 'finally' block | try sem except (ou com o except indentado errado) |
SyntaxError: expected ':' | Faltaram os dois-pontos do try ou do except |
O traceback aparece mesmo com try | O 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 motivo | except: ou except Exception: pegando um erro não previsto |
| Ctrl+C não para o programa | except: sozinho, dentro de um laço |
Exercícios
1. Complete o tratamento
Complete para tratar o erro de um número mal digitado:
2. O que aparece?
Quantas linhas este programa mostra?
try:
print("A")
x = int("oi")
print("B")
except ValueError:
print("C")
print("D")
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 dotryé pulado e oexceptroda. Sem erro, oexcepté 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 eguarda 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+breakpergunta de novo até a pessoa acertar.