Tratando exceções
Teoria
Reconhecendo exceções
Cara, nem sempre a gente pode garantir que um código que a gente escreveu vai funcionar sempre. Imagina esse programa como exemplo, meramente ilustrativo:
Nosso programa de exemplo perguntar ao usuário um número e calcula quanto é 30 dividido por esse número. Depois, esse resultado é exibido pro usuário. Beleza! Mas, cara, se o usuário digitar 0 vai dar treta nisso aí! Apesar disso, nosso código não necessariamente tá errado, entende?! Acontece que existem casos onde o código falha. Existem exceções, e essa é a palavra do dia.
Sabendo reconhecer que nosso código pode ter exceções no seu funcionamento, a gente pode tratar essas exceções a nossa maneira!
Tratando exceções
Primeiramente, deixa eu te falar o que significa tratar exceções. Tá ligado que quando algum erro acontece, aparece aquelas letrinhas vermelhas com o erro?! Então... Tratar exceções significa que a gente quer tratar manualmente esses erros. Em vez das letrinhas vermelhas, o código vai executar.
Pra isso, vamos aprender três novas palavras-chave: try, except e finally. O código abaixo ilustra o esqueleto das estruturas de tratamento de exceções, mas ainda vou detalhar os usos das palavras-chave.
Palavra-chave try
A palavra try (“tentar”, em inglês) é usada pra criar um bloco de código que talvez dê errado. Essa palavra chave é a alma do tratamento de exceções. Só da pra gente tratar exceções que estão dentro do bloco try.
Palavra-chave except
A gente usa a palavra-chave except (“exceção”, em inglês) pra definir o que deve acontecer caso dê erro! Isso significa que o código dentro desse bloco não vai ser executado caso tudo dê certo. Repara que no código de exemplo, a gente usou um print() dizendo que não foi possível calcular o resultado e definimos a variável “resultado” pra zero. Esse código só é executado quando o código no bloco try der treta e não funcionar.
Se a gente colocar o except, igual na imagem acima, QUALQUER erro que der no código do try vai simplesmente chamar o bloco except. No entanto, a gente também consegue especificar o erro que a gente quer tratar!
Nesse código, a gente tá especificando que só quer tratar o erro da divisão por zero. Então, qualquer outro erro seria tratado normalmente pelo Python (malditas letrinhas vermelhas)..
Mas repara que pra especificar o erro que você quer tratar, é preciso especificar o nome do erro! E aí você me pergunta: “cara, como diabos eu vou saber o nome do erro?”.
Bom, o macete é você não decorar, mas provocar o erro de propósito pra copiar seu nome! Olha, vou colocar na primeira linha do meu código uma conta que com certeza vai dar o erro de divisão por zero.
Quando tentei executar...
Olha ele aí... Conseguimos descobrir o nome do erro provocando o erro propositalmente! Malandramente! Hahaha
Obs: eu tô usando o erro de divisão por zero como um exemplo. Na verdade, essa estrutura funciona pra qualquer tipo de erro. Vou usar um outro erro pra exemplificar. Repara que a gente tá convertendo o resultado da função input() pra inteiro, mas caso o cara não digite um número, vai dar erro! Vamos descobrir qual o nome do erro.
Quando executei...
Pronto! Descobrimos que o nome desse erro é ValueError. Vamos especificar que nosso bloco exception só vai tratar esses dois tipos de erro agora...
Show! Agora, o bloco except vai tratar os dois casos: se o usuário digitar um número invalido ou se digitar zero. Por último, repara que dessa vez eu coloquei o linha que tem a função int(input()) dentro do bloco try. Isso porque só da pra gente tratar erros que acontecem dentro do bloco try, sacou?!
Palavra-chave finally
No bloco que a gente cria com a palavra-chave finally (“finalmente”, em inglês), o código vai ser executado SEMPRE. Independente de dar erro ou não no bloco try, o bloco finally vai ser executado no fim. Se o bloco try der erro, o bloco except vai ser executado ANTES do finally. Sacou?!
Na real, a ideia do bloco finally é garantir que o código vai ser executado. Depois que o erro aconteceu, pode ser que o programa siga firme e forte se você tratar aquela exceção, mas pode ser que o programa pare e dê aquele errinho das letras vermelhas. Independente disso, o bloco finally vai ser executado SEMPRE, ok?!
Usando só dois blocos
Beleza, agora que sacamos a utilidade dos três blocos, se liga que não é obrigatório a gente usar os três.
Olha, dessa forma a variável “resultado” vira zero caso dê algum erro no bloco try, mas não construímos nenhum bloco finally. Isso é ok! Assim como também é possível fazer o contrário. Olha de novo esse caso:
Se o número digitado for 0, a gente sabe que vai dar erro. Mas quando um erro ocorre, o programa imediatamente para.
Reparou que o último print() não foi executado?! Isso não acontece quando usamos o bloco finally, porque o que tá dentro do finally SEMPRE é executado, independente do que acontece no try.
O resultado:
Como não tratamos o erro no except, deu erro da mesma forma. Mas repara que mesmo assim o print() foi executado. Viu?!
Pra você mandar super bem na sua prova, é importante fazer os exercícios de fixação, ok?! To te esperando lá!
Palavra-chave finally
Usando só dois blocos
Exercícios Resolvidos
Exercício Resolvido #1
Elaboração própria.
Observe o seguinte programa:
Qual exceção será gerada durante a sua execução ?
Passo 1
Podemos observar com facilidade que o módulo math foi importado incorretamente ao utilizarmos o nome mathematics e por esse motivo que uma exceção é gerada.
Resposta
Logo, por termos uma única importação incorreta em nosso código, podemos afirmar que a exceção gerada é: ImportError.
Exercício Resolvido #2
Elaboração própria.
Observe o seguinte programa:
Escreva o que será impresso na tela caso o mesmo seja executado com o seguinte arquivo de entrada:
Passo 1
Bom, vamos lá!
A primeira coisa que nós iremos fazer é analisar o arquivo de entrada desse programa (program.in).
Com isso, sabemos que o programa terá a entrada de dois números, que são: 5 e 3.
Passo 2
Agora que já sabemos quais são as entradas, podemos executarmos o programa em busca de exceções.
Dentro de um bloco try o programa irá requisitar a entrada de dois números do tipo float para as variáveis e , sem problemas até aqui, uma vez que 5 e 3 estão presentes no conjunto dos números reais.
Passo 3
Em seguida, uma variável recebe o resultado da divisão entre e . Mais uma vez, tudo está tranquilo.
Passo 4
Por fim, temos a impressão da variável com precisão de duas casas decimais.
PERA!
Nós não temos uma variável e sim uma variável . Logo, uma exceção será gerada, mais precisamente a exceção NameError.
Passo 5
Agora ficou fácil, só nos resta analisarmos as exceções que foram tratadas.
Nós temos o tratamento de exceções do tipo ValueError, ZeroDivisionError e uma para todas as outras exceções.
Como a nossa exceção não é nem do tipo ValueError quanto do tipo ZeroDivisionError, o que será impresso na tela é aquilo executado no bloco onde temos apenas except.
Resposta
Portanto, o que será impresso na tela é: “Algo deu errado.”
Exercício Resolvido #3
Elaboração própria.
Um colega de classe tentou executar o seguinte programa:
E obteve a seguinte mensagem de erro:
Sem entender muito bem o significado dessa mensagem, ele decidiu pedir ajuda a você que estuda programação pelo Responde Aí.
Reescreva o código feito por ele de uma forma com que esse erro seja exibido de uma forma mais clara e amigável, e, consequentemente, possa ser resolvido pelo próprio colega.
Passo 1
Muito simples!
Ao observarmos a mensagem de erro, já sabemos de cara que devemos simplesmente tratar uma exceção FileNotFoundError.
Passo 2
Sendo assim, basta colocarmos o código dentro de um bloco try e, dentro de um bloco except para a exceção FileNotFoundError, escrevermos uma mensagem mais clara:
Resposta
Portanto, ao invés do nosso colega ter que decifrar o erro, ele saberá de cara que o arquivo o qual está tentando abrir não existe.
Exercício Resolvido #4
Elaboração própria.
Observe o seguinte programa:
Um dos problemas do código acima é que o mesmo além de possuir um erro lógico (execução de um comando de escrita em um arquivo em modo de leitura), abre um arquivo e tem a sua execução encerrada.
Incremente o código apresentado liberando da memória a referência do arquivo tanto nos casos de erro, quanto em execuções bem-sucedidas (caso o mesmo fosse aberto em modo de escrita).
Passo 1
Bom, a primeira coisa que devemos fazer é analisar o que o enunciado quer que nós façamos.
Quando o mesmo diz que devemos liberar da memória a referência do arquivo aberto, ele está nos dizendo que devemos utilizar a função close. Porém, isso deve ser feito “tanto nos casos de erro, quanto em execuções bem-sucedidas (caso o mesmo fosse aberto em modo de escrita).”
Passo 2
Sendo assim, podemos utilizar o comando finally, uma vez que ele será sempre executado, ou seja, tanto quando o bloco try não gerar exceções, quanto após a execução de algum bloco except.
Resposta
Portanto, após o incremento do bloco finally o programa ficará da seguinte forma: