Qual é o uso pretendido da else
cláusula opcional da try
declaração?
Qual é o uso pretendido da else
cláusula opcional da try
declaração?
Respostas:
As instruções no else
bloco são executadas se a execução cair na parte inferior do try
- se não houver exceção. Honestamente, nunca encontrei uma necessidade.
No entanto, o tratamento de exceções observa:
O uso da cláusula else é melhor do que adicionar código adicional à cláusula try, pois evita a captura acidental de uma exceção que não foi gerada pelo código que está sendo protegido pela instrução try ... exceto.
Portanto, se você possui um método que pode, por exemplo, lançar um IOError
e deseja capturar exceções, isso ocorre, mas há outra coisa que você deseja fazer se a primeira operação for bem-sucedida e não desejar capturar um IOError de nessa operação, você pode escrever algo como isto:
try:
operation_that_can_throw_ioerror()
except IOError:
handle_the_exception_somehow()
else:
# we don't want to catch the IOError if it's raised
another_operation_that_can_throw_ioerror()
finally:
something_we_always_need_to_do()
Se você apenas colocar another_operation_that_can_throw_ioerror()
depois operation_that_can_throw_ioerror
, except
ele capturaria os erros da segunda chamada. E se você colocar depois de todo o try
bloco, ele sempre será executado, e não até depois do finally
. O else
permite garantir
finally
bloco eIOError
s que levanta não é pego aquireturn
, continue
ou break
.
Há uma grande razão para usar else
- estilo e legibilidade. Geralmente, é uma boa idéia manter o código que pode causar exceções perto do código que lida com eles. Por exemplo, compare estes:
try:
from EasyDialogs import AskPassword
# 20 other lines
getpass = AskPassword
except ImportError:
getpass = default_getpass
e
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
else:
# 20 other lines
getpass = AskPassword
O segundo é bom quando except
não é possível retornar mais cedo ou repetir a exceção. Se possível, eu teria escrito:
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
return False # or throw Exception('something more descriptive')
# 20 other lines
getpass = AskPassword
Nota: Resposta copiada da duplicata postada recentemente aqui , portanto, todo esse material "AskPassword".
Um uso: teste algum código que deve gerar uma exceção.
try:
this_should_raise_TypeError()
except TypeError:
pass
except:
assert False, "Raised the wrong exception type"
else:
assert False, "Didn't raise any exception"
(Este código deve ser abstraído para um teste mais genérico na prática.)
Try-else do Python
Qual é o uso pretendido da
else
cláusula opcional da instrução try?
O uso pretendido é ter um contexto para que mais código seja executado, se não houver exceções nas quais se espera que sejam manipuladas.
Esse contexto evita o tratamento acidental de erros que você não esperava.
Mas é importante compreender as condições precisas que causam a cláusula else a prazo, porque return
, continue
e break
pode interromper o fluxo de controle para else
.
A else
declaração é executado se houver há exceções e se não for interrompida por um return
, continue
ou break
declaração.
A
else
cláusula opcional é executada se e quando o controle fluir no final datry
cláusula. *
(Negrito adicionado.) E a nota de rodapé diz:
* Atualmente, o controle “flui para fora da extremidade”, exceto no caso de uma exceção ou a execução de um
return
,continue
oubreak
declaração.
Requer pelo menos uma cláusula exceto anterior ( consulte a gramática ). Portanto, realmente não é "try-else", é "try-except-else (-finally)", com o else
(e finally
) sendo opcional.
O Tutorial do Python detalha o uso pretendido:
A instrução try ... except possui uma cláusula opcional else, que, quando presente, deve seguir todas as exceções. É útil para o código que deve ser executado se a cláusula try não gerar uma exceção. Por exemplo:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
O uso da cláusula else é melhor do que adicionar código adicional à cláusula try, pois evita a captura acidental de uma exceção que não foi gerada pelo código que está sendo protegido pela instrução try ... exceto.
else
versus código após o try
blocoSe você manipular um erro, o else
bloco não será executado. Por exemplo:
def handle_error():
try:
raise RuntimeError('oops!')
except RuntimeError as error:
print('handled a RuntimeError, no big deal.')
else:
print('if this prints, we had no error!') # won't print!
print('And now we have left the try block!') # will print!
E agora,
>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!
Try-except-else é ótimo para combinar o padrão EAFP com a digitação de pato :
try:
cs = x.cleanupSet
except AttributeError:
pass
else:
for v in cs:
v.cleanup()
Você pode achar que esse código ingênuo é bom:
try:
for v in x.cleanupSet:
v.clenaup()
except AttributeError:
pass
Essa é uma ótima maneira de ocultar acidentalmente erros graves no seu código. Digitei a limpeza lá, mas o AttributeError que me informaria está sendo engolido. Pior, e se eu o tivesse escrito corretamente, mas ocasionalmente o método de limpeza recebia um tipo de usuário com um atributo com nome errado, causando falhas silenciosas no meio do caminho e deixando um arquivo não fechado? Boa sorte na depuração dessa.
Acho que é realmente útil quando você precisa fazer a limpeza, mesmo que haja uma exceção:
try:
data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
handle_exception(e)
else:
do_stuff(data)
finally:
clean_up()
Mesmo que você não consiga pensar em usá-lo agora, pode apostar que deve haver um uso para ele. Aqui está uma amostra sem imaginação:
Com else
:
a = [1,2,3]
try:
something = a[2]
except:
print "out of bounds"
else:
print something
Sem else
:
try:
something = a[2]
except:
print "out of bounds"
if "something" in locals():
print something
Aqui você tem a variável something
definida se nenhum erro for gerado. Você pode remover isso fora do try
bloco, mas isso requer alguma detecção confusa se uma variável for definida.
something = a[2]; print something
dentro do bloco try:?
Há um bom exemplo disso try-else
no PEP 380 . Basicamente, tudo se resume a manipular exceções diferentes em diferentes partes do algoritmo.
É algo como isto:
try:
do_init_stuff()
except:
handle_init_suff_execption()
else:
try:
do_middle_stuff()
except:
handle_middle_stuff_exception()
Isso permite que você escreva o código de manipulação de exceção mais próximo de onde a exceção ocorre.
De erros e exceções # Manipulando exceções - docs.python.org
A
try ... except
declaração possui umaelse
cláusula opcional , que, quando presente, deve seguir todas, exceto as cláusulas. É útil para o código que deve ser executado se a cláusula try não gerar uma exceção. Por exemplo:for arg in sys.argv[1:]: try: f = open(arg, 'r') except IOError: print 'cannot open', arg else: print arg, 'has', len(f.readlines()), 'lines' f.close()
O uso da cláusula else é melhor do que adicionar código adicional à cláusula try, pois evita a captura acidental de uma exceção que não foi gerada pelo código que está sendo protegido pela instrução try ... exceto.
Olhando para a referência Python , parece que else
é executado depois try
quando não há exceção. A cláusula else opcional é executada se e quando o controle fluir no final da cláusula try. 2 Exceções na cláusula else não são tratadas pelas cláusulas anteriores, exceto.
Mergulhe no python tem um exemplo em que, se eu entendi direito, no try
bloco eles tentam importar um módulo, quando isso falha, você obtém exceção e vincula o padrão, mas quando funciona, você tem a opção de entrar no else
bloco e vincular o que é necessário (consulte link para o exemplo e explicação).
Se você tentou trabalhar em catch
bloco, isso poderia gerar outra exceção - acho que é aí que o else
bloco é útil.
try
bloco.
É isso aí. O bloco 'else' de uma cláusula try-except existe para o código que é executado quando (e somente quando) a operação tentada é bem-sucedida. Pode ser usado e pode ser abusado.
try:
fp= open("configuration_file", "rb")
except EnvironmentError:
confdata= '' # it's ok if the file can't be opened
else:
confdata= fp.read()
fp.close()
# your code continues here
# working with (possibly empty) confdata
Pessoalmente, gosto e uso quando apropriado. Agrupa semanticamente instruções.
Talvez um uso possa ser:
#debug = []
def debuglog(text, obj=None):
" Simple little logger. "
try:
debug # does global exist?
except NameError:
pass # if not, don't even bother displaying
except:
print('Unknown cause. Debug debuglog().')
else:
# debug does exist.
# Now test if you want to log this debug message
# from caller "obj"
try:
if obj in debug:
print(text) # stdout
except TypeError:
print('The global "debug" flag should be an iterable.')
except:
print('Unknown cause. Debug debuglog().')
def myfunc():
debuglog('Made it to myfunc()', myfunc)
debug = [myfunc,]
myfunc()
Talvez isso o leve também a um uso.
Eu achei a try: ... else:
construção útil na situação em que você está executando consultas de banco de dados e registrando os resultados dessas consultas em um banco de dados separado do mesmo tipo / tipo. Digamos que eu tenha muitos threads de trabalho, todos lidando com consultas de banco de dados enviadas a uma fila
#in a long running loop
try:
query = queue.get()
conn = connect_to_db(<main db>)
curs = conn.cursor()
try:
curs.execute("<some query on user input that may fail even if sanitized">)
except DBError:
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute("<update in DB log with record of failed query")
logcurs.close()
logconn.close()
else:
#we can't put this in main try block because an error connecting
#to the logging DB would be indistinguishable from an error in
#the mainquery
#We can't put this after the whole try: except: finally: block
#because then we don't know if the query was successful or not
logconn = connect_to_db(<logging db>)
logcurs = logconn.cursor()
logcurs.execute("<update in DB log with record of successful query")
logcurs.close()
logconn.close()
#do something in response to successful query
except DBError:
#This DBError is because of a problem with the logging database, but
#we can't let that crash the whole thread over what might be a
#temporary network glitch
finally:
curs.close()
conn.close()
#other cleanup if necessary like telling the queue the task is finished
Obviamente, se você pode distinguir entre as possíveis exceções que podem ser lançadas, não é necessário usá-lo, mas se o código que reage a um pedaço de código bem-sucedido pode lançar a mesma exceção que o pedaço bem-sucedido, e você não pode simplesmente deixe a segunda exceção possível ir ou retorne imediatamente com sucesso (o que mataria o encadeamento no meu caso), e isso será útil.
else
Muitas vezes, pode existir um bloco para complementar a funcionalidade que ocorre em cada except
bloco.
try:
test_consistency(valuable_data)
except Except1:
inconsistency_type = 1
except Except2:
inconsistency_type = 2
except:
# Something else is wrong
raise
else:
inconsistency_type = 0
"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""
Nesse caso, inconsistency_type
é definido em cada bloco exceto, para que o comportamento seja complementado no caso sem erro em else
.
Claro, estou descrevendo isso como um padrão que pode aparecer em seu próprio código algum dia. Nesse caso específico, você apenas define inconsistency_type
0 antes do try
bloco.
Aqui está outro lugar onde eu gosto de usar esse padrão:
while data in items:
try
data = json.loads(data)
except ValueError as e:
log error
else:
# work on the `data`
continue
- o padrão "sair cedo". Isso permite que você elimine a cláusula "else" e seu recuo, facilitando a leitura do código.
Um dos cenários de uso em que posso pensar é as exceções imprevisíveis, que podem ser contornadas se você tentar novamente. Por exemplo, quando as operações no bloco try envolvem números aleatórios:
while True:
try:
r = random.random()
some_operation_that_fails_for_specific_r(r)
except Exception:
continue
else:
break
Mas se a exceção puder ser prevista, você sempre deve escolher a validação antecipadamente em vez de uma exceção. No entanto, nem tudo pode ser previsto, portanto, esse padrão de código tem seu lugar.
break
interior try
no final, que é o IMO mais limpo e não precisa do else
. Também continue
não é realmente necessário, você pode apenas pass
.
Eu achei else
útil para lidar com um arquivo de configuração possivelmente incorreto:
try:
value, unit = cfg['lock'].split()
except ValueError:
msg = 'lock monitoring config must consist of two words separated by white space'
self.log('warn', msg)
else:
# get on with lock monitoring if config is ok
Uma exceção na leitura da lock
configuração desativa o monitoramento de bloqueios e o ValueErrors registra uma mensagem de aviso útil.
Eu adicionaria outro caso de uso que parece simples ao lidar com sessões de banco de dados:
# getting a DB connection
conn = db.engine.connect()
# and binding to a DB session
session = db.get_session(bind=conn)
try:
# we build the query to DB
q = session.query(MyTable).filter(MyTable.col1 == 'query_val')
# i.e retrieve one row
data_set = q.one_or_none()
# return results
return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]
except:
# here we make sure to rollback the transaction,
# handy when we update stuff into DB
session.rollback()
raise
else:
# when no errors then we can commit DB changes
session.commit()
finally:
# and finally we can close the session
session.close()
O else:
bloco é confuso e (quase) inútil. Também faz parte das declarações for
e while
.
Na verdade, mesmo em uma if
declaração, o else:
pode ser abusado de maneiras realmente terríveis, criando bugs que são muito difíceis de encontrar.
Considere isto.
if a < 10:
# condition stated explicitly
elif a > 10 and b < 10:
# condition confusing but at least explicit
else:
# Exactly what is true here?
# Can be hard to reason out what condition is true
Pense duas vezes else:
. Geralmente é um problema. Evite-o, exceto em uma if
declaração e, mesmo assim, considere documentar a else
condição - para torná-la explícita.
if x > 0: return "yes"
e if x <= 0: return "no"
. Agora uma pessoa vem e muda uma das condições para dizer, x > 1
mas esquece de mudar a outra. Como é esse número reduzido de bugs que seriam cometidos. if else
as cláusulas às vezes têm muitas linhas separadas. DRY é uma boa prática, muito mais frequentemente do que não, na verdade. (desculpe pelo post duplo).