Tratamento de Exceções

Exceções em Python

Alguma vez, enquanto testando seus programas Python, você recebeu na tela uma mensagem de erro parecida com a seguinte?

Traceback (most recent call last):
File "<stdin>", line 1, in \<module\>
IndexError: list index out of range

Ou com essa?

Traceback (most recent call last):
File "<stdin>", line 1, in \<module\>
NameError: name 'x' is not defined

Em caso positivo, Python lhe atirou uma exceção e você nada fez com ela. Imagine que você esteja escrevendo uma calculadora bem simples usando Python como linguagem para a implementação. Tudo que ela faz são as quatro operações básicas (soma, subtração, multiplicação, divisão) e o cálculo da raiz quadrada. Ao terminar a implementação, você mostra ao seu irmão, cheio de orgulho a calculadora que acabou de implementar. O guri já vai direto ao ponto, seleciona a opção de raiz quadrada e fornece o valor:

-1

Antes de ele pressionar o enter para confirmar a operação, você já percebeu que esqueceu de tratar a entrada do usuário em operações de raiz quadrada. Você já dever saber que raiz quadrada de número negativo só é possível se utilizarmos números complexos, não? E como tal, já imagina que a função sqrt() do módulo math que você usou vai ter problemas para lidar com isso. Quando seu irmão confirma a operação, lá vem a mensagem:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error

ValueError. Guarde esse nome para daqui a pouco…

Prontamente, você abre o seu editor de texto e corrige o código, verificando se número do qual será calculada a raiz quadrada é menor que zero.

if op1 < 0:
    print 'Atenção! Não é possível calcular a raiz quadrada de um número negativo!'

OK, tudo certo. Seu programa voltou a funcionar e o chato do seu irmão pode parar de te incomodar.

Mas e o tal do ValueError que pedi para você lembrar? Então, ele é uma exceção que Python usa para lhe indicar que algo anormal aconteceu durante a execução do seu código. E ela não serve apenas como uma simples mensagem de erro. Muito pelo contrário, o objetivo é que o programador, sabendo que determinado trecho de código pode disparar uma exceção dessas, utilize uma estrutura específica para tratamento de exceções (try-except, para os íntimos).

try-except

A estrutura básica do bloco try-except é:

try:
    # código a ser executado "sob vigilância"
except Exception:
    # caso ocorrer alguma exceção no código acima,
    # trate-a aqui.

O código localizado entre o try: e o except é executado de forma que se algo de errado acontecer ali, o except Exception vai imediatamente chamar o código localizado abaixo de si para realizar o tratamento da exceção. Ou seja, se algo de errado acontecer na linha 2, as linhas 4-5 serão executadas para tratar esse erro.

Resolvendo o problema

Sabendo um pouco como as exceções funcionam, e sabendo que a tentativa de calcular a raiz quadrada de um número negativo gera um ValueError, você irá envolver o trecho de código que poderá gerar tal exceção em um bloco try-except. Veja:

try:
    result = math.sqrt(op1)
except Exception:
    print 'Atenção! Não é possível calcular a raiz quadrada de um número negativo!'

Ao executar o código acima e passar um valor negativo para o segundo operando da divisão, você vê que funcionou. Agora, ao invés da mensagem feia na tela, você recebe sua mensagem de erro personalizada (mais tarde veremos que o mecanismo de exceção não serve só para mandarmos mensagens de erro para o usuário, mas também (e principalmente) para tratar as exceções.).

E aí, a mala do seu irmão vem testar o programa novamente. Desta vez, ele fornece o próprio nome como entrada para a raiz quadrada. Você já imagina que lá vem outro erro, afinal Python não é mágico para calcular a raiz quadrada de "Joaozinho". 😛 Para sua surpresa, eis que aparece na tela a mensagem:

Atenção! Não é possível calcular a raiz quadrada de um número negativo!

Aí você e seu irmão não entendem mais nada, afinal ninguém passou um número negativo como entrada. Na realidade, o que aconteceu é que o bloco:

except Exception:
    print 'Atenção! Não é possível calcular a raiz quadrada de um número negativo!'

significa mais ou menos o seguinte: se ocorrer uma exceção qualquer, nas linhas entre o try e a linha except Exception:, trate-a com o bloco de código que vem após o : (que é o nosso print).

Perceba, except Exception: irá agarrar QUALQUER exceção que Python atirar, pois Exception é a classe-base da qual todas as outras exceções são descendentes. Se você quiser que a mensagem 'Atenção! Não é possível calcular a raiz quadrada de um número negativo!' seja mostrada ao usuário apenas quando for passado um valor negativo à raiz quadrada, então teremos que especializar o nosso tratamento de exceções, isto é, especificar qual é a exceção que queremos tratar realmente. Como vimos anteriormente, a exceção que Python atira quando ocorre uma tentativa de cálculo de raiz quadrada de um valor negativo se chama ValueError. Então, fazemos:

except ValueError:
    print 'Atenção! Não é possível calcular a raiz quadrada de um número negativo!'

Opa, agora sim, a mensagem só vai aparecer quando houver o erro supracitado. É claro que Python não fornece uma exceção para cada um de todos os possíveis tipos de erros, mas ele já vem de fábrica com um bocado. Às vezes, pode ser preciso que você implemente sua própria exceção, como por exemplo, se o seu programa recebesse do usuário o valor do CPF. Você poderia criar uma exceção CPFInvalidError, ou seja qual for o nome que quisesse dar. Mas, criação de exceções customizadas é assunto para outro post.

Tratando as Exceções

Talvez você esteja pensando: “mas jogar uma mensagem de erro na tela não é bem o que eu entendia por tratamento de exceção”. E você está certo, podemos fazer muito mais do que isso. No exemplo anterior, podemos corrigir o erro do usuário, transformando o número negativo em um número positivo. O código a ser executado ao tratarmos uma exceção é código Python como qualquer outro, então você pode fazer o que for preciso para corrigir o problema.

try:
    result = math.sqrt(op1)
except ValueError:
    print 'Atenção! Tranformando entrada negativa em número positivo.'
    op1 = -op1
    result = math.sqrt(op1)

Nota: Esse exemplo é meramente didático, para você entender como funcionam as exceções e como elas podem ter vários usos, mas ele não é um bom exemplo do uso de exceções. Leia a seção das boas práticas e entenda por quê.

Assim, além de avisar ao usuário sobre o erro, já estamos fazendo a correção, de forma que o programa possa seguir seu fluxo normal de execução.

Por fim, talvez você esteja se perguntando como vai adivinhar o nome da exceção para colocar no except. Uma das formas de descobrir quais exceções determinadas funções/operações disparam quando acontece uma situação imprevista, é lendo a documentação da função/operação que você estiver utilizando. Outra modo de descobrir é encarando a mensagem de erro quando acontece uma exceção não tratada. Por exemplo:

>>> math.sqrt("hello")
Traceback (most recent call last):
File "", line 1, in
TypeError: a float is required

Ao executar o comando math.sqrt("hello"), acabamos de descobrir que ele atira uma exceção do tipo TypeError quando recebe uma entrada de um tipo diferente do numérico.

Boas práticas no tratamento de exceções

Agora que você (e o seu irmão) já estão manjando como funcionam exceções e o que dá pra fazer com elas, está na hora de conhecerem algumas boas práticas, alguns conselhos que geralmente é uma boa idéia seguir, na labuta diária com exceções.

  1. Não use exceções para o fluxo principal da aplicação. Ou, dizendo de outro modo, use exceções apenas para o que é realmente excepcional, inesperado. A idéia é que, enquanto seu programa esteja executando o caminho feliz, isto é, enquanto está tudo dentro do esperado — o céu está azul, os planetas estão girando em torno do Sol e a Lua em torno da Terra — ele não precise voltar atrás na sua execução devido a uma exceção ter sido lançada. Isso é uma boa idéia por que o seu código ficará mais direto ao ponto e você ainda evita o eventual custo adicional que programar “orientado a exceções” acarreta (por exemplo, por tentar fazer uma coisa, voltar e tentar de novo). No exemplo utilizado nesse post, poderíamos simplesmente evitar que a exceção ocorra fazendo uma simples verificação sobre o valor, com um if.
  2. Interceptar exceções somente quando você sabe como tratá-la. Ou seja, não adianta de muita coisa encher o seu código de try-except se tudo o que o seu tratamento de exceção faz é imprimir uma mensagem de erro na tela. Trate exceções específicas, e quando você souber como lidar com ela. Senão você pode acabar obscurecendo uma exceção importante (como KeyboardInterrupt ou SystemExit, por exemplo) que era melhor lançá-la mesmo.
  3. Conhecer as exceções da biblioteca-padrão. Tire um tempo para dar uma olhada com cuidado nos tipos de exceções que Python já traz de fábrica, pois além de conhecer algumas exceções que você pode usar no seu programa, você passa a conhecer os principais tipos de situações inesperadas que o seu programa está sujeito.

6 comentários sobre “Tratamento de Exceções

  1. Gostaria de aproveitar a oportunidade para fazer uma pergunta:
    Eu tenho um formulário em wx.python e gostaria de quando preencher um campo chave e pressionar enter fazer uma validação no código digitado para no caso de o código já existir, exibir todos os dados do registro no formulário.

    O problema é que não consigo interceptar o evento da tecla Enter.

    Será que alguém pode me dar uma Dica?

    Carlos
    Email: carlosrabreu@hotmail.com
    Obrigado

  2. Pingback: Programando para a Web em Python | Python Help

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s