Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial programming >> python

Evitando Armadilhas Comuns: Tratamento Adequado de Exceções em Python

Evitando Armadilhas Comuns: Tratamento Adequado de Exceções em Python

Vejo muitas pessoas lidando com exceções da maneira errada. Talvez isso se aplique a você também. A seguinte situação soa familiar?

Você está escrevendo algum código, mas sabe que a biblioteca que está usando pode lançar uma exceção. Você não lembra exatamente qual. Neste ponto, é tentador usar os chamados blocos catch-all e seguir para as partes divertidas.

A pior maneira de fazer

O pior que você pode fazer é criar um bloco try-except que captura qualquer coisa. Por catch-all, quero dizer algo como:

try:
    ...
except:
    pass

Blocos catch-all como estes são ruins porque:

  1. Você não tem ideia de quais outras exceções podem ser lançadas (mais sobre isso depois).
  2. Estamos escondendo a exceção silenciosamente usando pass em vez de registrar o erro.

Além disso, um except vazio capturará tudo, incluindo KeyboardInterrupt (Ctrl+C), SystemExit e até NameError! Isso significa que o seguinte código não pode ser parado limpo:

from time import sleep
while True:
    try:
        print("Tente me parar")
        sleep(1)
    except:
        print("Não... me... pare... agora!")

Sinta-se à vontade para testar. Você precisa fechar a janela do terminal ou matar o processo Python para parar este programa.

Uma maneira um pouco melhor de capturar todas as exceções

Em contraste, ao usar except Exception, embora ainda seja uma maneira rápida e suja de capturar muitas exceções, pelo menos você conseguirá parar o processo em execução corretamente:

from time import sleep
while True:
    try:
        print("Tente me parar")
        sleep(1)
    except Exception:
        print("Ok, eu paro!")

Ao capturar Exception, você não capturará SystemExit, KeyboardInterrupt e outras tais exceções. Por que é isso, você pergunta?

Todas as exceções herdam de uma classe chamada BaseException. Segundo a documentação oficial: "Em uma declaração try com uma cláusula except que menciona uma classe particular, essa cláusula também lida com quaisquer classes de exceção derivadas daquela classe." Um except vazio é equivalente a except BaseException, portanto, capturará todas as exceções possíveis.

Em contraste, a classe Exception é definida como: "Todas as exceções embutidas, não de saída do sistema, são derivadas desta classe. Todas as exceções definidas pelo usuário também devem ser derivadas desta classe."

Fica ainda pior

No exemplo a seguir, usamos a biblioteca os para obter o diretório de trabalho atual. No entanto, meus dedos gordos cometeram um erro de digitação:

import os
try:
    working_dir = os.getcdw()
    print(working_dir)
except:
    print('erro')

Como os.getcdw não é uma função no módulo os, um NameError é lançado. Em vez de falhar, a cláusula except captura o erro, imprime 'erro' e o programa continua apesar do nosso erro flagrante. Infelizmente, este não é solucionável capturando Exception também!

Aparentemente, nosso pequeno truque do passo um não é uma correção para todos os nossos problemas. Então, o que devemos fazer?

Capture apenas o que você pode lidar

Uma frase frequentemente ouvida sobre exceções é: capture o que você pode lidar. Muitos desenvolvedores são tentados a lidar diretamente com exceções, enquanto muitas vezes é melhor deixar a exceção propagar para uma parte do seu programa que pode realmente lidar com ela.

Por exemplo, considere a parte de um editor de texto que abre e carrega arquivos, vamos chamá-la de classe OpenFile. Se o usuário solicitou abrir um arquivo que não existe, você pode lidar diretamente com esse erro ou deixá-lo propagar.

Neste caso, é melhor propagar a exceção para o chamador, porque OpenFile não tem ideia de quão ruim essa exceção é para o chamador. O chamador pode lidar com a situação de várias maneiras:

De qualquer forma, não cabe à classe OpenFile decidir o que fazer em caso de FileNotFoundError.

Uma exceção deve sempre ser propagada? Não. Uma possível exceção que pode ser tratada na classe FileOpen é o TimeoutError. Você pode querer tentar novamente algumas vezes, por exemplo, sem incomodar o chamador com o erro. Esta é uma exceção que OpenFile pode lidar, então está OK capturá-la e tentar novamente.

Conclusão

Você nunca deve capturar mais exceções do que pode lidar. Blocos except genéricos são uma receita para bugs e código imprevisível. Em outras palavras: capture o que você pode lidar.

Se você escrever seu código com o mantra 'capture o que você pode lidar' em mente, escrever blocos catch-all está quebrando todas as regras. Então, por favor, pare de fazer isso. Como exercício, você pode revisitar algum código existente e ver se pode ser melhorado com este novo conhecimento!

python

  1. Dominando Operadores em Python: Guia Completo
  2. Dominando Erros e Exceções Built-in do Python
  3. Dominando Exceções Customizadas em Python: Guia Prático
  4. Obter Data e Hora Atuais em Python: Guia Prático
  5. Dominando Tratamento Multi-Exceção em Java
  6. Dominando o Treinamento de CMMS: Engajando Equipes e Aumentando Retenção
  7. Função Print() do Python: Guia Prático com Exemplos
  8. Dicionários Python: Adicionando, Atualizando e Gerenciando Pares Chave/Valor
  9. Print em Python Sem Nova Linha: Dominando o Parâmetro end e Outras Técnicas
  10. Calculando Médias em Python: Guia Prático