"Não sei se é por ignorância, mas não gosto desse tipo de programação, pois está usando exceções para executar o controle de fluxo".
No mundo Python, o uso de exceções para controle de fluxo é comum e normal.
Até os desenvolvedores principais do Python usam exceções para controle de fluxo e esse estilo é fortemente incorporado à linguagem (ou seja, o protocolo iterador usa StopIteration para sinalizar o encerramento do loop).
Além disso, o estilo try-except é usado para evitar as condições de corrida inerentes a algumas das construções "olhar antes de pular" . Por exemplo, testar os.path.exists resulta em informações que podem estar desatualizadas quando você as usa. Da mesma forma, Queue.full retorna informações que podem estar obsoletas. O estilo try-except-else produzirá código mais confiável nesses casos.
"Entendo que exceções não são erros, elas devem ser usadas apenas para condições excepcionais"
Em alguns outros idiomas, essa regra reflete suas normas culturais, como refletidas em suas bibliotecas. A "regra" também se baseia em parte nas considerações de desempenho para esses idiomas.
A norma cultural Python é um pouco diferente. Em muitos casos, você deve usar exceções para o fluxo de controle. Além disso, o uso de exceções no Python não reduz a velocidade do código circundante e do código de chamada, como ocorre em algumas linguagens compiladas (ou seja, o CPython já implementa código para verificação de exceções a cada passo, independentemente de você usar ou não as exceções).
Em outras palavras, seu entendimento de que "exceções são excepcionais" é uma regra que faz sentido em outras linguagens, mas não no Python.
"No entanto, se estiver incluído no próprio idioma, deve haver uma boa razão para isso, não é?"
Além de ajudar a evitar condições de corrida, as exceções também são muito úteis para extrair loops externos de manipulação de erros. Essa é uma otimização necessária em linguagens interpretadas que não tendem a ter movimento de código invariável de loop automático .
Além disso, as exceções podem simplificar bastante o código em situações comuns em que a capacidade de lidar com um problema está muito longe de onde ele surgiu. Por exemplo, é comum ter um código de interface de usuário de nível superior que chame o código da lógica comercial, que por sua vez chama rotinas de baixo nível. As situações que surgem nas rotinas de baixo nível (como registros duplicados para chaves exclusivas nos acessos ao banco de dados) só podem ser tratadas no código de nível superior (como solicitar ao usuário uma nova chave que não entre em conflito com as chaves existentes). O uso de exceções para esse tipo de fluxo de controle permite que as rotinas de nível médio ignorem completamente o problema e sejam bem dissociadas desse aspecto do controle de fluxo.
Há um bom post sobre a indispensabilidade de exceções aqui .
Além disso, consulte esta resposta de estouro de pilha: as exceções são realmente para erros excepcionais?
"Qual é o motivo da tentativa, exceto outra coisa?"
A própria cláusula else é interessante. É executado quando não há exceção, mas antes da cláusula final. Esse é o seu objetivo principal.
Sem a cláusula else, a única opção para executar código adicional antes da finalização seria a prática desajeitada de adicionar o código à cláusula try. Isso é desajeitado porque corre o risco de gerar exceções no código que não foi projetado para ser protegido pelo bloco de tentativa.
O caso de uso da execução de código desprotegido adicional antes da finalização não ocorre com muita frequência. Portanto, não espere ver muitos exemplos no código publicado. É um tanto raro.
Outro caso de uso para a cláusula else é executar ações que devem ocorrer quando nenhuma exceção ocorre e que não ocorrem quando as exceções são tratadas. Por exemplo:
recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')
Outro exemplo ocorre em corredores mais unidos:
try:
tests_run += 1
run_testcase(case)
except Exception:
tests_failed += 1
logging.exception('Failing test case: %r', case)
print('F', end='')
else:
logging.info('Successful test case: %r', case)
print('.', end='')
Por fim, o uso mais comum de uma cláusula else em um bloco de teste é para um pouco de embelezamento (alinhando os resultados excepcionais e não excepcionais no mesmo nível de indentação). Esse uso é sempre opcional e não é estritamente necessário.
Um
try
bloco permite que você lide com um erro esperado. Oexcept
bloco deve capturar apenas exceções com as quais você está preparado para lidar. Se você manipular um erro inesperado, seu código poderá fazer a coisa errada e ocultar bugs.Uma
else
cláusula será executada se não houver erros e, ao não executar esse código notry
bloco, você evita a captura de um erro inesperado. Mais uma vez, capturar um erro inesperado pode ocultar bugs.Exemplo
Por exemplo:
O conjunto "try, except" possui duas cláusulas opcionais
else
efinally
. Então é verdadetry-except-else-finally
.else
avaliará apenas se não houver exceção dotry
bloco. Permite simplificar o código mais complicado abaixo:portanto, se compararmos
else
com a alternativa (que pode criar bugs), veremos que ela reduz as linhas de código e podemos ter uma base de código mais legível, sustentável e com menos bugs.finally
finally
será executado independentemente do que for, mesmo que outra linha esteja sendo avaliada com uma declaração de retorno.Dividido com pseudo-código
Pode ajudar a decompor isso, na menor forma possível que demonstre todos os recursos, com comentários. Suponha que esse pseudocódigo sintaticamente correto (mas não executável, a menos que os nomes sejam definidos) esteja em uma função.
Por exemplo:
É verdade que, em vez disso, poderíamos incluir o código no
else
blocotry
, onde seria executado se não houvesse exceções, mas e se esse código em si gerar uma exceção do tipo que estamos capturando? Deixá-lo notry
bloco esconderia esse bug.Queremos minimizar as linhas de código no
try
bloco para evitar capturar exceções que não esperávamos, sob o princípio de que, se nosso código falhar, queremos que falhe alto. Essa é uma prática recomendada .No Python, a maioria das exceções são erros.
Podemos visualizar a hierarquia de exceções usando pydoc. Por exemplo, no Python 2:
ou Python 3:
Nos dará a hierarquia. Podemos ver que muitos tipos de
Exception
erros, embora o Python os use para coisas como finalizarfor
loops (StopIteration
). Esta é a hierarquia do Python 3:Um comentarista perguntou:
Não, você não retorna a exceção, basta aumentá-la com um simples
raise
para preservar o rastreamento de pilha.Ou, no Python 3, você pode criar uma nova exceção e preservar o backtrace com o encadeamento de exceções:
Eu elaboro em minha resposta aqui .
fonte
raise
encadeamento simples ou excepcional - mas isso é mais sobre o tópico e é abordado aqui: stackoverflow.com/q/2052390/541136 - provavelmente removerei esses comentários depois de ter visto que você os viu.O Python não concorda com a idéia de que as exceções devem ser usadas apenas para casos excepcionais; na verdade, o idioma é 'peça perdão, não permissão' . Isso significa que o uso de exceções como parte rotineira do seu controle de fluxo é perfeitamente aceitável e, de fato, incentivado.
Isso geralmente é uma coisa boa, pois trabalhar dessa maneira ajuda a evitar alguns problemas (como um exemplo óbvio, as condições de corrida geralmente são evitadas) e tende a tornar o código um pouco mais legível.
Imagine que você tem uma situação em que recebe algumas informações do usuário que precisam ser processadas, mas tem um padrão que já está processado. A
try: ... except: ... else: ...
estrutura cria um código muito legível:Compare com o funcionamento em outros idiomas:
Observe as vantagens. Não há necessidade de verificar se o valor é válido e analisá-lo separadamente, eles são feitos uma vez. O código também segue uma progressão mais lógica, o caminho principal do código é o primeiro, seguido por 'se não funcionar, faça isso'.
O exemplo é naturalmente um pouco artificial, mas mostra que há casos para essa estrutura.
fonte
A resposta para isso é que depende do contexto. Se você fizer isto:
Isso demonstra que você não conhece muito bem o Python. Essa funcionalidade está encapsulada no
dict.get
método:O bloco
try
/except
é uma maneira muito mais visual e desordenada de escrever o que pode ser executado com eficiência em uma única linha com um método atômico. Existem outros casos em que isso é verdade.No entanto, isso não significa que devemos evitar todo o tratamento de exceções. Em alguns casos, é preferível evitar as condições da corrida. Não verifique se existe um arquivo, apenas tente abri-lo e pegue o IOError apropriado. Por uma questão de simplicidade e legibilidade, tente encapsular isso ou fatorá-lo como apropriado.
Leia o Zen de Python , entendendo que existem princípios que estão em tensão, e tenha cuidado com o dogma que depende muito de qualquer uma das declarações nele contidas.
fonte
Veja o exemplo a seguir, que ilustra tudo sobre try-except-else-finalmente:
Implementá-lo e passar por:
fonte
Você deve ter cuidado ao usar o bloco final, pois não é a mesma coisa que usar outro bloco na tentativa, exceto. O bloco final será executado independentemente do resultado da tentativa, exceto.
Como todos observaram, o uso do bloco else faz com que seu código seja mais legível e só é executado quando uma exceção não é lançada
fonte
Sempre que você vir isso:
Ou até isso:
Considere isso:
fonte
Só porque ninguém mais postou essa opinião, eu diria
Ao contrário das palavras-chave
try
,except
efinally
, o significado daelse
cláusula não é auto-evidente; é menos legível. Como não é usado com muita frequência, as pessoas que leem seu código desejam verificar novamente os documentos para garantir que entendem o que está acontecendo.(Estou escrevendo esta resposta precisamente porque encontrei uma
try/except/else
na minha base de código e isso causou um momento wtf e me forçou a pesquisar no Google).Então, onde quer que eu veja códigos como o exemplo do OP:
Eu preferiria refatorar para
<1> retorno explícito, mostra claramente que, no caso de exceção, terminamos de trabalhar
<2> como um bom efeito colateral menor, o código que costumava estar no
else
bloco é deduzido por um nível.fonte
Este é meu trecho simples de como entender o bloco try-except-else-finalmente no Python:
Vamos tentar div 1/1:
Vamos tentar div 1/0
fonte
OP, VOCÊ ESTÁ CORRETO. O resto após try / except em Python é feio . leva a outro objeto de controle de fluxo, onde não é necessário:
Um equivalente totalmente claro é:
Isso é muito mais claro do que uma cláusula else. O resto após try / except não é frequentemente escrito, portanto leva um momento para descobrir quais são as implicações.
Só porque você pode fazer uma coisa, não significa que você deve fazer uma coisa.
Muitos recursos foram adicionados aos idiomas porque alguém achou que poderia ser útil. O problema é que, quanto mais recursos, as coisas menos claras e óbvias são porque as pessoas geralmente não usam esses sinos e assobios.
Apenas meus 5 centavos aqui. Eu tenho que vir atrás e limpar muito código escrito por desenvolvedores de faculdades do primeiro ano que pensam que são inteligentes e querem escrever código de uma maneira super apertada e super eficiente, quando isso faz uma bagunça para tentar ler / modificar mais tarde. Eu voto pela legibilidade todos os dias e duas vezes aos domingos.
fonte
print
declaração que falha. O que acontece sex = blah()
retornar astr
, mas a sua declaração de impressão forprint 'just succeeded with blah. x == %d' % x
? Agora você tem umTypeError
ser gerado onde não está preparado para lidar com um; você está inspecionandox = blah()
para encontrar a fonte da exceção, e ela nem está lá. Já fiz isso (ou o equivalente) mais de uma vez, ondeelse
me impediria de cometer esse erro. Agora eu sei melhor. :-Delse
cláusula não é uma afirmação bonita e, até que você esteja acostumado, não é intuitiva. Mas, então, nem foifinally
quando eu comecei a usá-lo ...else
cláusula não são capturadas peloexcept
.x = blah()
para encontrar a fonte da exceção".traceback
Por que você inspecionaria a fonte da exceção de um lugar errado?