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 try
Palavra-chave except
Palavra-chave finally
Usando só dois blocos
Exercícios Resolvidos
Exercício Resolvido #1
Elaboração própria.
Faça um programa que calcule a raiz quadrada de um número e trate os casos em que .
Passo 1
Vamos começar com um simples programa que calcula a raiz quadrada de um número:
Passo 2
Vamos agora inserir dentro de um bloco try os comandos que podem gerar erros, nesse caso a raiz quadrada de um número negativo, para que os mesmos possam ser executados sob vigilância do programa:
Passo 3
Por se tratar de uma raiz quadrada, não pode assumir valores negativos, caso contrário teremos um erro do tipo ValueError. Por esse motivo, colocaremos um bloco de exceção para tratar erros do tipo ValueError imprimindo uma mensagem de que algo deu errado:
Resposta
Ei, a resposta está no passo a passo :)
Exercício Resolvido #2
Elaboração própria.
Faça um programa que calcule a divisão de dois números e e trate os casos em que .
Passo 1
Vamos começar com um simples programa que calcula a divisão de dois números:
Passo 2
Vamos agora inserir dentro de um bloco try os comandos que podem gerar erros, nesse caso uma divisão por 0, para que os mesmos possam ser executados sob vigilância do programa:
Passo 3
Por se tratar de uma divisão entre e , não pode ser igual a 0, caso contrário teremos um erro do tipo ZeroDivisionError. Por esse motivo, colocaremos um bloco de exceção para tratar erros do tipo ZeroDivisionError imprimindo uma mensagem de que não se pode dividir um número por 0:
Resposta
Ei, a resposta está no passo a passo :)
Exercício Resolvido #3
Elaboração própria.
Observe o programa abaixo:
Reescreva esse código de uma forma com que ele seja capaz de tratar a inserção de um caractere por parte do usuário.
Passo 1
Antes de tudo, devemos saber que o erro gerado a partir da inserção de um caractere em uma requisição de número inteiro é o ValueError.
Passo 2
Tendo isso em mente, podemos simplesmente codificar o nosso programa dentro de um bloco try para que exceções do tipo ValueError sejam reconhecidas e devidamente tratadas:
No nosso caso, simplesmente imprimimos uma mensagem dizendo que o valor inserido é inválido.
Resposta
Ei, a resposta está no passo a passo :)
Exercício Resolvido #4
Elaboração própria.
Observe o seguinte programa:
Tendo em mente que ao executá-lo a exceção NameError é gerada, reescreva o código de forma com que tal exceção seja tratada.
Passo 1
Como o próprio enunciado já diz o nome da exceção gerada na execução, podemos simplesmente codificar o nosso programa dentro de um bloco try para que exceções do tipo ValueError sejam reconhecidas e devidamente tratadas:
Nesse caso, basicamente imprimimos na tela que não foi possível achar uma referência (para ) no nosso código.
Resposta
Ei, a resposta está no passo a passo :)
Exercício Resolvido #5
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 #6
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 #7
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 #8
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: