Devo sempre especificar um tipo de exceção nas instruções `except`?


93

Ao usar o PyCharm IDE, o uso de except:sem um tipo de exceção aciona um lembrete do IDE de que essa cláusula de exceção é Too broad.

Devo ignorar este conselho? Ou é Pythônico sempre especificar o tipo de exceção?


Se você quiser saber mais sobre isso do que as respostas, google engolindo exceções. Você pode fazer todos os tipos de coisas interessantes que você deve e não deve fazer ao lado deles. Cheiro de código é outro.
Tony Hopkinson de

Respostas:


89

Quase sempre é melhor especificar um tipo de exceção explícito. Se você usar uma except:cláusula simples , poderá acabar capturando exceções diferentes das que espera detectar - isso pode ocultar bugs ou tornar mais difícil depurar programas quando eles não estão fazendo o que você espera.

Por exemplo, se você estiver inserindo uma linha em um banco de dados, talvez queira capturar uma exceção que indica que a linha já existe, para que possa fazer uma atualização.

try:
    insert(connection, data)
except:
    update(connection, data)

Se você especificar um bare except:, também obterá um erro de soquete indicando que o servidor de banco de dados caiu. É melhor capturar apenas as exceções que você sabe como lidar - geralmente é melhor para o programa falhar no ponto da exceção do que continuar, mas se comportar de maneiras estranhas e inesperadas.

Um caso em que você pode querer usar um except:servidor vazio é no nível superior de um programa que você precisa estar sempre em execução, como um servidor de rede. Mas então, você precisa ter muito cuidado para registrar as exceções, caso contrário, será impossível descobrir o que está errado. Basicamente, deve haver apenas no máximo um lugar em um programa que faça isso.

Um corolário de tudo isso é que seu código nunca deve funcionar raise Exception('some message')porque força o código do cliente a ser usado except:(ou o except Exception:que é quase tão ruim). Você deve definir uma exceção específica para o problema que deseja sinalizar (talvez herdando de alguma subclasse de exceção embutida como ValueErrorou TypeError). Ou você deve gerar uma exceção interna específica. Isso permite que os usuários do seu código sejam cuidadosos ao capturar apenas as exceções que desejam tratar.


7
1 Muito verdadeiro. Ainda mais divertido com o exemplo: except:também pega (entre muitas outras coisas) NameErrore AttributeError, então, se você digitar algo errado no trybloco (por exemplo, sua função "inserir" é realmente chamada insert_oneporque alguém não valorizou a consistência tanto quanto deveria), sempre silenciosamente tenta update().

1
E quando você precisar garantir que uma exceção não seja lançada acima do site da chamada atual? Ou seja - eu peguei todas as exceções específicas que posso prever sendo lançadas, agora preciso adicionar que "se isso que eu não pensei é lançado, preciso registrá-lo antes que elimine o contexto de execução em execução" (como main())?
Adam Parkin

Esse é o tipo de situação de que estou falando no parágrafo que começa com 'Um caso ...'. Definitivamente é necessário às vezes, mas qualquer programa deve realmente ter apenas um lugar para fazer isso. E você precisa ter cuidado para deixar claro no log o que está acontecendo, caso contrário, você terá um tempo frustrante tentando descobrir por que seu programa não está lidando com o evento / solicitação / qualquer coisa corretamente.
babbageclunk

3
@delnan: Pior que isso. except Exception:vai pegar NameErrore AttributeErrortambém. O que o torna except:tão ruim é que ele captura coisas que não devem ser detectadas, por exemplo SystemExit(levantadas quando você chama exitou sys.exit, e agora você evitou uma saída intencional) e KeyboardInterrupt(novamente, se o usuário clicar Ctrl-C, você provavelmente não quer continuar correndo apenas para irritá-los). Apenas o último faz algum sentido real para ser capturado, e deve ser capturado explicitamente. Pelo menos except Exception:permite que esses dois se propaguem normalmente.
ShadowRanger

38

Você não deve ignorar o conselho que o intérprete lhe dá.

Do Guia de estilo PEP-8 para Python:

Ao capturar exceções, mencione exceções específicas sempre que possível, em vez de usar uma cláusula exceto:.

Por exemplo, use:

 try:
     import platform_specific_module 
 except ImportError:
     platform_specific_module = None 

Uma simples exceção: cláusula capturará exceções SystemExit e KeyboardInterrupt, tornando mais difícil interromper um programa com Control-C, e pode disfarçar outros problemas. Se você quiser capturar todas as exceções que sinalizam erros de programa, use exceto Exceção: (bare except é equivalente a except BaseException :).

Uma boa regra prática é limitar o uso de cláusulas "exceto" a dois casos:

Se o handler de exceção for imprimir ou registrar o traceback; pelo menos o usuário saberá que ocorreu um erro. Se o código precisa fazer algum trabalho de limpeza, mas permite que a exceção se propague para cima com aumento. tente ... finalmente pode ser a melhor maneira de lidar com este caso.



9

Não é específico para Python isso.

O objetivo das exceções é lidar com o problema o mais próximo possível de onde ele foi causado.

Portanto, você mantém o código que poderia, em circunstâncias excepcionais, acionar o problema e a resolução "lado a lado".

O problema é que você não pode saber todas as exceções que podem ser lançadas por um trecho de código. Tudo o que você pode saber é que, se for uma exceção, digamos, um arquivo não encontrado, você pode interceptá-lo e solicitar que o usuário obtenha um que execute ou cancele a função.

Se você colocar try catch round that, então não importa o problema que houve em sua rotina de arquivo (somente leitura, permissões, UAC, não é realmente um pdf, etc), todos irão cair em seu arquivo não encontrado catch, e seu usuário está gritando "mas está aí, esse código é uma porcaria"

Agora, há algumas situações em que você pode pegar tudo, mas elas devem ser escolhidas conscientemente.

Eles são pegos, desfazem alguma ação local (como criar ou bloquear um recurso, (abrir um arquivo no disco para escrever, por exemplo), então você lança a exceção novamente, para ser tratada em um nível superior)

O outro você é que você não se importa por que deu errado. Imprimindo, por exemplo. Você pode ter uma pegadinha que diz: Há algum problema com sua impressora, resolva-o e não elimine o aplicativo por causa disso. De forma semelhante, se o seu código executasse uma série de tarefas separadas usando algum tipo de programação, você não gostaria que tudo morresse, porque uma das tarefas falhou.

Nota Se você fizer o acima, não posso recomendar algum tipo de registro de exceção, por exemplo, tente catch log end, altamente suficiente.


Eu diria que é uma questão de equilíbrio. Você precisa detectar a exceção cedo o suficiente para poder se recuperar dela e tarde o suficiente para saber aonde ir com ela. É por isso que o tratamento de exceções Java causa tantos estragos, porque você tem que embrulhar novamente a exceção a cada etapa e você perde informações.
dhill

2
+1 para "você não se importa por que deu errado." Estou usando em vários lugares em torno de uma única linha de código, onde analiso uma data / hora de um URL. A biblioteca de análise de data / hora de terceiros não lista todas as exceções que pode lançar (encontrei OverflowError e TypeError além do ValueError padrão, mas provavelmente há mais) e, de qualquer forma, realmente não me importo por que uma exceção foi lançada, eu só quero fornecer uma mensagem de erro razoável para o usuário dizendo que algo está errado com a data / hora.
Michael Rodby

3

Você também pegará, por exemplo, Control-C com isso, então não faça isso a menos que "jogue" novamente. No entanto, nesse caso, você deve preferir usar "finalmente".


3

Sempre especifique o tipo de exceção, existem muitos tipos você não quiser pegar, como SyntaxError, KeyboardInterrupt, MemoryErroretc.


2
que usar except Exception:evitaria os tipos acima que não queremos capturar?
HorseloverFat de

except Exceptionestá bem.
Ulrich Eckhardt de

4
@HorseloverFat: except Exceptionpega SyntaxErrore MemoryErrorporque é sua classe base. KeyboardInterrupt, SystemExit(gerado por sys.exit()) não são capturados (são subclasses de BaseException imediatas)
jfs

soa como se não fosse ideal então - melhor especificar com mais precisão.
HorseloverFat

3

Aqui estão os lugares onde eu uso, exceto sem tipo

  1. prototipagem rápida e suja

Esse é o principal uso em meu código para exceções não verificadas

  1. função main () de nível superior, onde registro todas as exceções não detectadas

Eu sempre adiciono isso, para que o código de produção não espalhe rastreamentos de pilha

  1. entre camadas de aplicação

Tenho duas maneiras de fazer isso:

  • Primeira maneira de fazer isso: quando uma camada de nível superior chama uma função de nível inferior, ela agrupa as chamadas em exceções digitadas para lidar com as exceções de nível inferior "superiores". Mas eu adiciono uma instrução exceto genérica, para detectar exceções de nível inferior não tratadas nas funções de nível inferior.

Eu prefiro assim, acho mais fácil detectar quais exceções deveriam ter sido capturadas apropriadamente: eu "vejo" melhor o problema quando uma exceção de nível inferior é registrada por um nível superior

  • Segunda maneira de fazer isso: cada função de nível superior de camadas de nível inferior tem seu código embrulhado em uma exceção genérica, para capturar todas as exceções não tratadas nessa camada específica.

Alguns colegas de trabalho preferem assim, pois mantém exceções de nível inferior nas funções de nível inferior, onde "pertencem".


-14

Experimente isto:

try:
    #code
except ValueError:
    pass

Obtive a resposta neste link, se mais alguém tiver esse problema, dê uma olhada

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.