Costumo ver comentários sobre outras questões do Stack Overflow sobre como o uso de except: pass
é desencorajado. Por que isso é ruim? Às vezes, simplesmente não me importo com os erros e quero continuar com o código.
try:
something
except:
pass
Por que o uso de um except: pass
bloco é ruim? O que a torna ruim? É o fato de eu estar pass
com um erro ou de except
algum erro?
logging
módulo no nível DEBUG para evitar que eles fluam na produção, mas mantenha-os disponíveis no desenvolvimento.Respostas:
Como você adivinhou corretamente, existem dois lados: Capturar qualquer erro especificando nenhum tipo de exceção depois
except
e simplesmente transmiti-lo sem executar nenhuma ação.Minha explicação é "um pouco" mais longa - então tl; dr se resume a isso:
Mas vamos entrar em detalhes:
Não pegue nenhum erro
Ao usar um
try
bloco, você geralmente faz isso porque sabe que há uma chance de uma exceção ser lançada. Como tal, você também já tem uma idéia aproximada do que pode quebrar e que exceção pode ser lançada. Nesses casos, você captura uma exceção porque pode se recuperar positivamente dela. Isso significa que você está preparado para a exceção e tem algum plano alternativo que seguirá no caso dessa exceção.Por exemplo, quando você pede ao usuário para inserir um número, pode converter a entrada usando o
int()
que pode gerar aValueError
. Você pode recuperá-lo facilmente, simplesmente pedindo ao usuário para tentar novamente, então capturarValueError
e solicitar ao usuário novamente seria um plano apropriado. Um exemplo diferente seria se você quiser ler alguma configuração de um arquivo e esse arquivo não existir. Por ser um arquivo de configuração, você pode ter alguma configuração padrão como fallback, portanto, o arquivo não é exatamente necessário. Portanto, pegar umFileNotFoundError
e simplesmente aplicar a configuração padrão seria um bom plano aqui. Agora, em ambos os casos, temos uma exceção muito específica que esperamos e temos um plano igualmente específico para se recuperar dela. Como tal, em cada caso, explicitamente apenasexcept
que certas exceção.No entanto, se quiséssemos capturar tudo , então - além das exceções que estamos preparados para recuperar - também há uma chance de obtermos exceções que não esperávamos e das quais realmente não podemos recuperar; ou não deve se recuperar.
Vamos dar o exemplo do arquivo de configuração acima. No caso de um arquivo ausente, acabamos de aplicar nossa configuração padrão e, posteriormente, podemos decidir salvar automaticamente a configuração (portanto, da próxima vez, o arquivo existe). Agora imagine que temos um
IsADirectoryError
, ou umPermissionError
em vez de. Nesses casos, provavelmente não queremos continuar; ainda poderíamos aplicar nossa configuração padrão, mas mais tarde não conseguiremos salvar o arquivo. E é provável que o usuário tenha também uma configuração personalizada, portanto, o uso dos valores padrão provavelmente não é desejado. Portanto, gostaríamos de informar o usuário sobre isso imediatamente e provavelmente abortar a execução do programa também. Mas isso não é algo que queremos fazer em algum lugar profundo dentro de uma pequena parte do código; isso é algo de importância no nível do aplicativo, por isso deve ser tratado no topo - deixe a exceção surgir.Outro exemplo simples também é mencionado no documento de idiomas do Python 2 . Aqui, existe um simples erro de digitação no código que causa a sua quebra. Como estamos capturando todas as exceções, também captamos
NameError
s eSyntaxError
s . Ambos são erros que acontecem a todos nós durante a programação; e ambos são erros que absolutamente não queremos incluir ao enviar o código. Mas como também os capturamos, nem saberemos que eles ocorreram lá e perderemos qualquer ajuda para depurá-lo corretamente.Mas também existem exceções mais perigosas para as quais não estamos preparados. Por exemplo, SystemError é geralmente algo que acontece raramente e que não podemos realmente planejar; significa que há algo mais complicado acontecendo, algo que provavelmente nos impede de continuar a tarefa atual.
De qualquer forma, é muito improvável que você esteja preparado para tudo em uma parte do código em pequena escala; portanto, é nesse ponto que você deve capturar apenas as exceções para as quais está preparado. Algumas pessoas sugerem, pelo menos, capturar
Exception
, pois não incluirão coisas comoSystemExit
eKeyboardInterrupt
que, por design, devem finalizar seu aplicativo, mas eu argumentaria que isso ainda é muito inespecífico. Só existe um lugar onde eu pessoalmente aceito capturarException
ou qualquerexceção, e isso está em um único manipulador de exceções no nível de aplicativo global que tem o único objetivo de registrar qualquer exceção para a qual não estávamos preparados. Dessa forma, ainda podemos reter o máximo de informações sobre exceções inesperadas, que podemos usar para estender nosso código para lidar com elas explicitamente (se conseguirmos recuperar delas) ou - no caso de um bug - para criar casos de teste para garantir isso não vai acontecer novamente. Mas, é claro, isso só funciona se capturarmos as exceções que já esperávamos; portanto, as que não esperávamos naturalmente borbulhariam.Tente evitar a passagem, exceto blocos
Ao capturar explicitamente uma pequena seleção de exceções específicas, há muitas situações em que ficaremos bem simplesmente sem fazer nada. Nesses casos, apenas ter
except SomeSpecificException: pass
está bem. Na maioria das vezes, porém, esse não é o caso, pois provavelmente precisamos de algum código relacionado ao processo de recuperação (como mencionado acima). Isso pode ser, por exemplo, algo que repete a ação novamente ou para configurar um valor padrão.Se esse não for o caso, por exemplo, porque nosso código já está estruturado para repetir até que seja bem-sucedido, apenas passar é bom o suficiente. Tomando nosso exemplo acima, podemos solicitar ao usuário que digite um número. Como sabemos que os usuários gostam de não fazer o que pedimos, podemos colocá-lo em um loop em primeiro lugar, para que fique assim:
Como continuamos tentando até que nenhuma exceção seja lançada, não precisamos fazer nada de especial no bloco exceto, então isso é bom. Mas, é claro, alguém pode argumentar que pelo menos queremos mostrar ao usuário alguma mensagem de erro para lhe dizer por que ele precisa repetir a entrada.
Em muitos outros casos, apenas passar
except
um sinal de que não estávamos realmente preparados para a exceção que estamos capturando. A menos que essas exceções sejam simples (comoValueError
ouTypeError
), e a razão pela qual podemos passar seja óbvia, tente evitar apenas passar. Se não houver realmente nada a fazer (e você tiver certeza absoluta disso), considere adicionar um comentário sobre o motivo; caso contrário, expanda o bloco de exceção para incluir algum código de recuperação.except: pass
O pior ofensor é a combinação de ambos. Isso significa que estamos apanhando qualquer erro de boa vontade, embora não estejamos absolutamente preparados para isso e também não fazemos nada a respeito. Você, pelo menos, deseja registrar o erro e também provavelmente re-aumentá-lo para finalizar o aplicativo (é improvável que você possa continuar normalmente após um MemoryError). Só passar não só manterá o aplicativo um pouco vivo (dependendo de onde você captura, é claro), mas também jogará fora todas as informações, tornando impossível descobrir o erro - o que é especialmente verdadeiro se você não o descobrir.
Portanto, o ponto principal é: Capture apenas as exceções que você realmente espera e está preparado para se recuperar; todos os outros provavelmente são erros que você deve corrigir ou algo para o qual não está preparado. Passar exceções específicas é bom se você realmente não precisa fazer algo sobre elas. Em todos os outros casos, é apenas um sinal de presunção e de preguiça. E você definitivamente quer consertar isso.
fonte
except
, mas depois chamamraise
sem argumentos para continuar deixando a exceção aparecer, encerrando o aplicativo. Gostei: ianbicking.org/blog/2007/09/re-raising-exceptions.html . Parece uma exceção sólida à regra de não usar o cobertorexcept
.raise
. Normalmente, você faria isso apenas em alguns locais do aplicativo para registrar a exceção.O principal problema aqui é que ele ignora todo e qualquer erro: Sem memória, a CPU está queimando, o usuário quer parar, o programa quer sair, o Jabberwocky está matando os usuários.
Isso é demais. Na sua cabeça, você está pensando "Quero ignorar esse erro de rede". Se algo inesperado der errado, seu código continuará silenciosamente e quebrará de maneiras completamente imprevisíveis que ninguém pode depurar.
É por isso que você deve limitar-se a ignorar especificamente apenas alguns erros e deixar o resto passar.
fonte
Executar o seu pseudo-código literalmente não gera nenhum erro:
como se fosse um pedaço de código perfeitamente válido, em vez de lançar a
NameError
. Espero que não seja isso que você deseja.fonte
Isso captura todas as exceções possíveis, incluindo
GeneratorExit
,,KeyboardInterrupt
eSystemExit
- que são exceções que você provavelmente não pretende capturar. É o mesmo que pegarBaseException
.As versões mais antigas da documentação dizem :
Hierarquia de exceção do Python
Se você capturar uma classe de exceção pai, também captura todas as classes filho. É muito mais elegante capturar apenas as exceções que você está preparado para lidar.
Aqui está a hierarquia de exceção do Python 3 - você realmente quer pegá-los todos ?:
Não faça isso
Se você estiver usando esta forma de tratamento de exceções:
Então você não poderá interromper seu
something
bloco com Ctrl-C. Seu programa ignorará todas as exceções possíveis dentro dotry
bloco de código.Aqui está outro exemplo que terá o mesmo comportamento indesejável:
Em vez disso, tente capturar apenas a exceção específica que você sabe que está procurando. Por exemplo, se você sabe que pode receber um erro de valor em uma conversão:
Explicação adicional com outro exemplo
Você pode estar fazendo isso porque está
UnicodeError
usando a Web scraping e obtendo, digamos, um , mas porque usou a captura de exceção mais ampla, seu código, que pode ter outras falhas fundamentais, tentará executar até a conclusão, desperdiçando largura de banda , tempo de processamento, desgaste do equipamento, falta de memória, coleta de dados de lixo etc.Se outras pessoas estão pedindo que você preencha para que possam confiar no seu código, entendo que estou sendo obrigado a lidar com tudo. Mas se você estiver disposto a falhar ruidosamente à medida que se desenvolver, terá a oportunidade de corrigir problemas que só podem aparecer intermitentemente, mas que seriam erros dispendiosos a longo prazo.
Com um tratamento de erro mais preciso, o código pode ser mais robusto.
fonte
Então, aqui está a minha opinião. Sempre que encontrar um erro, você deve fazer algo para lidar com isso, por exemplo, escrevê-lo no arquivo de log ou algo mais. Pelo menos, informa que costumava haver um erro.
fonte
Você deve usar pelo menos
except Exception:
para evitar exceções do sistema comoSystemExit
ouKeyboardInterrupt
. Aqui está o link para documentos.Em geral, você deve definir explicitamente as exceções que deseja capturar, para evitar capturar exceções indesejadas. Você deve saber quais exceções você ignora .
fonte
Primeiro, ele viola dois princípios do Zen of Python :
O que isso significa é que você intencionalmente comete o erro silenciosamente. Além disso, você não sabe qual erro ocorreu exatamente, porque
except: pass
capturará qualquer exceção.Segundo, se tentarmos abstrair-se do Zen de Python e falar em termos de apenas sanidade, você deve saber que o uso o
except:pass
deixa sem conhecimento e controle em seu sistema. A regra geral é gerar uma exceção, se ocorrer um erro, e tomar as ações apropriadas. Se você não souber com antecedência, quais devem ser as ações, pelo menos registre o erro em algum lugar (e é melhor redigitar a exceção):Mas, geralmente, se você tentar capturar alguma exceção, provavelmente está fazendo algo errado!
fonte
A
except:pass
construção basicamente silencia todas e quaisquer condições excepcionais que surgem enquanto o código coberto notry:
bloco está sendo executado.O que torna essa má prática é que ela geralmente não é o que você realmente deseja. Mais frequentemente, está surgindo alguma condição específica que você deseja silenciar e
except:pass
é um instrumento muito contundente. Ele fará o trabalho, mas também ocultará outras condições de erro que você provavelmente não previu, mas que pode muito bem querer lidar de alguma outra maneira.O que torna isso particularmente importante no Python é que, pelos idiomas dessa linguagem, as exceções não são necessariamente erros . Eles são frequentemente usados dessa maneira, é claro, assim como na maioria dos idiomas. Mas o Python, em particular, ocasionalmente os usou para implementar um caminho de saída alternativo de algumas tarefas de código que não fazem parte do caso de execução normal, mas ainda é conhecido por surgir de tempos em tempos e pode até ser esperado na maioria dos casos.
SystemExit
já foi mencionado como um exemplo antigo, mas o exemplo mais comum hoje em dia pode serStopIteration
. O uso de exceções dessa maneira causou muita controvérsia, especialmente quando os iteradores e geradores foram introduzidos pela primeira vez no Python, mas eventualmente a idéia prevaleceu.fonte
O motivo número 1 já foi declarado - oculta erros que você não esperava.
(# 2) - Torna seu código difícil para outras pessoas lerem e entenderem. Se você capturar uma FileNotFoundException ao tentar ler um arquivo, é bastante óbvio para outro desenvolvedor que funcionalidade o bloco 'catch' deve ter. Se você não especificar uma exceção, precisará de comentários adicionais para explicar o que o bloco deve fazer.
(# 3) - Demonstra programação lenta. Se você usar o try / catch genérico, isso indica que você não entende os possíveis erros de tempo de execução no seu programa ou que não sabe quais exceções são possíveis no Python. A captura de um erro específico mostra que você entende o seu programa e o intervalo de erros que o Python gera. É mais provável que outros desenvolvedores e revisores de código confiem no seu trabalho.
fonte
Então, qual saída esse código produz?
Agora imagine o bloco
try
-except
é centenas de linhas de chamadas para uma hierarquia complexa de objetos e é chamado no meio da árvore de chamadas de grandes programas. Quando o programa dá errado, onde você começa a procurar?fonte
Em geral, você pode classificar qualquer erro / exceção em uma das três categorias :
Fatal : Não é sua culpa, você não pode evitá-los, não pode se recuperar deles. Você certamente não deve ignorá-los, continuar e deixar seu programa em um estado desconhecido. Apenas deixe o erro finalizar o seu programa, não há nada que você possa fazer.
Boneheaded : Sua própria falha, provavelmente devido a um erro de supervisão, bug ou programação. Você deve corrigir o erro. Novamente, você certamente não deve ignorar e continuar.
Exógenos : você pode esperar esses erros em situações excepcionais, como arquivo não encontrado ou conexão encerrada . Você deve lidar explicitamente com esses erros, e somente estes.
Em todos os casos
except: pass
, apenas deixará seu programa em um estado desconhecido, onde poderá causar mais danos.fonte
Simplificando, se uma exceção ou erro for lançado, algo está errado. Pode não ser algo muito errado, mas criar, lançar e capturar erros e exceções apenas para o uso de instruções goto não é uma boa idéia, e raramente é feito. 99% das vezes, havia um problema em algum lugar.
Problemas precisam ser resolvidos. Assim como na vida, na programação, se você deixar os problemas em paz e tentar ignorá-los, eles não desaparecerão por conta própria muitas vezes; em vez disso, aumentam e se multiplicam. Para impedir que um problema cresça em você e atinja novamente mais adiante, você 1) elimina-o e limpa a bagunça depois, ou 2) o contém e limpa a bagunça depois.
Ignorar exceções e erros e deixá-los assim é uma boa maneira de experimentar vazamentos de memória, excelentes conexões com o banco de dados, bloqueios desnecessários nas permissões de arquivos, etc.
Em raras ocasiões, o problema é tão minúsculo, trivial e - além de precisar de um bloco de tentativa ... autônomo , que realmente não há bagunça a ser limpa depois. Essas são as únicas ocasiões em que essa prática recomendada não se aplica necessariamente. Na minha experiência, isso geralmente significa que tudo o que o código está fazendo é basicamente mesquinho e perdoável, e algo como tentativas de repetição ou mensagens especiais não valem nem a complexidade nem a manutenção do encadeamento.
Na minha empresa, a regra é quase sempre fazer algo em um bloco de captura e, se você não fizer nada, deve sempre colocar um comentário com uma boa razão para não fazê-lo. Você nunca deve passar ou deixar um bloco de captura vazio quando houver algo a ser feito.
fonte
Na minha opinião, os erros têm uma razão para aparecer, que parece estúpido, mas é assim que é. Uma boa programação apenas gera erros quando você precisa lidar com eles. Além disso, como li há algum tempo, "a declaração pass é uma declaração que mostra o código será inserida mais tarde"; portanto, se você quiser ter uma declaração exceto vazia, sinta-se à vontade para fazê-lo, mas, para um bom programa, haverá falta uma parte. porque você não lida com as coisas que deveria ter. As exceções aparentes oferecem a chance de corrigir dados de entrada ou alterar sua estrutura de dados para que essas exceções não ocorram novamente (mas na maioria dos casos (exceções de rede, exceções de entrada gerais)) indicam que as próximas partes do programa não serão executadas corretamente. Por exemplo, um NetworkException pode indicar uma conexão de rede interrompida e o programa não pode enviar / receber dados nas próximas etapas do programa.
Mas usar um bloco de aprovação para apenas um bloco de execução é válido, porque você ainda diferencia entre os tipos de exceções; portanto, se você colocar todos os blocos de exceção em um, ele não estará vazio:
pode ser reescrito dessa maneira:
Portanto, mesmo vários blocos de exceção com instruções de passe podem resultar em código, cuja estrutura lida com tipos especiais de exceções.
fonte
Todos os comentários apresentados até o momento são válidos. Sempre que possível, você precisa especificar exatamente qual exceção deseja ignorar. Sempre que possível, você precisa analisar o que causou a exceção e ignorar apenas o que pretendia ignorar, e não o resto. Se a exceção faz o aplicativo "travar espetacularmente", seja assim, porque é muito mais importante saber o inesperado que aconteceu quando aconteceu do que ocultar que o problema já ocorreu.
Com tudo isso dito, não tome nenhuma prática de programação como um fator primordial. Isso é estúpido. Sempre há a hora e o local para fazer o bloco ignorar todas as exceções.
Outro exemplo de primordial idiota é o uso do
goto
operador. Quando eu estava na escola, nosso professor nos ensinou ogoto
operador apenas para mencionar que você nunca deve usá-lo, NUNCA. Não acredite nas pessoas dizendo que o xyz nunca deve ser usado e não pode haver um cenário em que seja útil. Sempre existe.fonte
Lidar com erros é muito importante na programação. Você precisa mostrar ao usuário o que deu errado. Em muito poucos casos, você pode ignorar os erros. É uma prática de programação muito ruim.
fonte
Como ainda não foi mencionado, é melhor usar o estilo
contextlib.suppress
:Observe que, no exemplo fornecido, o estado do programa permanece o mesmo, independentemente da exceção. Ou seja,
somefile.tmp
sempre se torna inexistente.fonte