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:
- Não pegue nenhum erro . Sempre especifique de quais exceções você está preparado para recuperar e somente as captura.
- Tente evitar a passagem, exceto blocos . A menos que seja explicitamente desejado, esse geralmente não é um bom sinal.
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 a ValueError
. Você pode recuperá-lo facilmente, simplesmente pedindo ao usuário para tentar novamente, então capturar ValueError
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 um FileNotFoundError
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 apenas except
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 e SyntaxError
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 como SystemExit
e KeyboardInterrupt
que, por design, devem finalizar seu aplicativo, mas eu argumentaria que isso ainda é muito inespecífico. Só existe um lugar onde eu pessoalmente aceito capturar Exception
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:
def askForNumber ():
while True:
try:
return int(input('Please enter a number: '))
except ValueError:
pass
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 (como ValueError
ou TypeError
), 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.
logging
módulo no nível DEBUG para evitar que eles fluam na produção, mas mantenha-os disponíveis no desenvolvimento.