Qual é o uso pretendido da elsecláusula opcional da trydeclaração?
Qual é o uso pretendido da elsecláusula opcional da trydeclaração?
Respostas:
As instruções no elsebloco 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 IOErrore 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, exceptele capturaria os erros da segunda chamada. E se você colocar depois de todo o trybloco, ele sempre será executado, e não até depois do finally. O elsepermite garantir
finallybloco eIOErrors que levanta não é pego aquireturn, continueou 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 exceptnã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
elseclá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, continuee breakpode interromper o fluxo de controle para else.
A elsedeclaração é executado se houver há exceções e se não for interrompida por um return, continueou breakdeclaração.
A
elsecláusula opcional é executada se e quando o controle fluir no final datryclá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,continueoubreakdeclaraçã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.
elseversus código após o tryblocoSe você manipular um erro, o elsebloco 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 somethingdefinida se nenhum erro for gerado. Você pode remover isso fora do trybloco, mas isso requer alguma detecção confusa se uma variável for definida.
something = a[2]; print somethingdentro do bloco try:?
Há um bom exemplo disso try-elseno 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 ... exceptdeclaração possui umaelseclá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 tryquando 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 trybloco 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 elsebloco e vincular o que é necessário (consulte link para o exemplo e explicação).
Se você tentou trabalhar em catchbloco, isso poderia gerar outra exceção - acho que é aí que o elsebloco é útil.
trybloco.
É 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.
elseMuitas vezes, pode existir um bloco para complementar a funcionalidade que ocorre em cada exceptbloco.
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_type0 antes do trybloco.
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.
breakinterior tryno final, que é o IMO mais limpo e não precisa do else. Também continuenã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 lockconfiguraçã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 fore while.
Na verdade, mesmo em uma ifdeclaraçã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 ifdeclaração e, mesmo assim, considere documentar a elsecondiçã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 > 1mas esquece de mudar a outra. Como é esse número reduzido de bugs que seriam cometidos. if elseas 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).