Qual è l'uso previsto della elseclausola facoltativa della trydichiarazione?
Qual è l'uso previsto della elseclausola facoltativa della trydichiarazione?
Risposte:
Le istruzioni nel elseblocco vengono eseguite se l'esecuzione cade dal fondo del try- se non ci sono eccezioni. Onestamente, non ho mai trovato un bisogno.
Tuttavia, note sulla gestione delle eccezioni :
L'uso della clausola else è meglio dell'aggiunta di codice aggiuntivo alla clausola try perché evita di catturare accidentalmente un'eccezione che non è stata sollevata dal codice protetto dall'istruzione try ... tranne.
Quindi, se hai un metodo che potrebbe, ad esempio, lanciare un IOErrore vuoi catturare le eccezioni che genera, ma c'è qualcos'altro che vuoi fare se la prima operazione ha esito positivo e non vuoi catturare un IOError da quell'operazione, potresti scrivere qualcosa del genere:
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 lo metti another_operation_that_can_throw_ioerror()dopo operation_that_can_throw_ioerror, exceptcatturerebbe gli errori della seconda chiamata. E se lo metti dopo l'intero tryblocco, verrà sempre eseguito e non fino a dopo il finally. Il elseconsente di assicurarsi
finallyblocco eIOErrors genera non vengono catturati quireturn, continueo break.
C'è un grande motivo per usarlo else: stile e leggibilità. In genere è una buona idea mantenere il codice che può causare eccezioni vicino al codice che li tratta. Ad esempio, confronta questi:
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
Il secondo è utile quando exceptnon è possibile tornare in anticipo o rilanciare l'eccezione. Se possibile, avrei scritto:
try:
from EasyDialogs import AskPassword
except ImportError:
getpass = default_getpass
return False # or throw Exception('something more descriptive')
# 20 other lines
getpass = AskPassword
Nota: risposta copiata dal duplicato pubblicato di recente qui , quindi tutta questa roba "AskPassword".
Un uso: prova del codice che dovrebbe sollevare un'eccezione.
try:
this_should_raise_TypeError()
except TypeError:
pass
except:
assert False, "Raised the wrong exception type"
else:
assert False, "Didn't raise any exception"
(Questo codice dovrebbe essere estratto in pratica in un test più generico.)
Python try-else
Qual è l'uso previsto della
elseclausola opzionale dell'istruzione try?
L'uso previsto è quello di avere un contesto per l'esecuzione di più codice se non ci sono eccezioni in cui dovrebbe essere gestito.
Questo contesto evita la gestione accidentale di errori non previsti.
Ma è importante capire le precise condizioni che causano la clausola altro da corsa, perché return, continuee breakin grado di interrompere il flusso di controllo a else.
L' elseistruzione viene eseguita se non ci sono nessun eccezioni e, se non interrotto da una return, continueo breakdichiarazione.
La
elseclausola opzionale viene eseguita se e quando il controllo scorre fuori dalla fine dellatryclausola. *
(Grassetto aggiunto.) E la nota a piè di pagina dice:
* Attualmente, il controllo “scorre fuori dall'estremità” salvo nel caso di un'eccezione o l'esecuzione di una
return,continueobreakeconomico.
Richiede almeno una clausola precedente tranne ( vedi la grammatica ). Quindi in realtà non è "try-else", è "try-tranne-else (-finally)", con il else(e finally) essendo facoltativo.
Il tutorial di Python elabora l'uso previsto:
L'istruzione try ... tranne ha una clausola else facoltativa che, quando presente, deve seguire tutte le clausole tranne. È utile per il codice che deve essere eseguito se la clausola try non genera un'eccezione. Per esempio:
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()L'uso della clausola else è meglio dell'aggiunta di codice aggiuntivo alla clausola try perché evita di catturare accidentalmente un'eccezione che non è stata sollevata dal codice protetto dall'istruzione try ... tranne.
elserispetto al codice che segue il trybloccoSe si gestisce un errore, il elseblocco non verrà eseguito. Per esempio:
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 adesso,
>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!
Try-tranne-else è ottimo per combinare il modello EAFP con la tipizzazione anatra :
try:
cs = x.cleanupSet
except AttributeError:
pass
else:
for v in cs:
v.cleanup()
Potresti pensare che questo codice ingenuo vada bene:
try:
for v in x.cleanupSet:
v.clenaup()
except AttributeError:
pass
Questo è un ottimo modo per nascondere accidentalmente gravi bug nel tuo codice. Ho ripulito la pulizia lì, ma l'attributo AttributeError che mi avrebbe fatto sapere è stato ingoiato. Peggio ancora, se avessi scritto correttamente, ma il metodo di pulizia veniva occasionalmente passato a un tipo di utente che aveva un attributo errato, causando un errore silenzioso a metà strada e lasciando un file chiuso? Buona fortuna debug quello.
Trovo davvero utile quando hai la pulizia da fare che deve essere fatta anche se c'è un'eccezione:
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()
Anche se non riesci a pensare a un uso in questo momento, puoi scommettere che ci deve essere un uso per questo. Ecco un esempio privo di fantasia:
Con else:
a = [1,2,3]
try:
something = a[2]
except:
print "out of bounds"
else:
print something
Senza else:
try:
something = a[2]
except:
print "out of bounds"
if "something" in locals():
print something
Qui hai la variabile somethingdefinita se non viene generato alcun errore. Puoi rimuoverlo al di fuori del tryblocco, ma richiede una certa confusione se viene definita una variabile.
something = a[2]; print somethingall'interno di try: block?
C'è un bell'esempio di try-elsein PEP 380 . Fondamentalmente, si tratta di fare una diversa gestione delle eccezioni in diverse parti dell'algoritmo.
È qualcosa del genere:
try:
do_init_stuff()
except:
handle_init_suff_execption()
else:
try:
do_middle_stuff()
except:
handle_middle_stuff_exception()
Ciò consente di scrivere il codice di gestione delle eccezioni più vicino al punto in cui si verifica l'eccezione.
Da errori ed eccezioni # Gestione delle eccezioni - docs.python.org
La
try ... exceptdichiarazione ha unaelseclausola facoltativa , che, quando presente, deve seguire tutte tranne le clausole. È utile per il codice che deve essere eseguito se la clausola try non genera un'eccezione. Per esempio: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()L'uso della clausola else è meglio dell'aggiunta di codice aggiuntivo alla clausola try perché evita di catturare accidentalmente un'eccezione che non è stata sollevata dal codice protetto dall'istruzione try ... tranne.
Guardando il riferimento Python sembra che elsevenga eseguito dopo che trynon c'è eccezione. La clausola else opzionale viene eseguita se e quando il controllo scorre fuori dalla fine della clausola try. 2 Le eccezioni nella clausola else non sono gestite dalle clausole precedenti tranne.
Immergersi in Python ha un esempio in cui, se ho capito bene, nel tryblocco cercano di importare un modulo, quando ciò fallisce si ottiene un'eccezione e si associa l'impostazione predefinita, ma quando funziona hai un'opzione per andare in elseblocco e associare ciò che è richiesto (vedi link per l'esempio e la spiegazione).
Se hai provato a fare un lavoro in catchblocco, potrebbe generare un'altra eccezione: immagino che sia qui che il elseblocco è utile.
tryblocco.
Questo è tutto. Il blocco 'else' di una clausola try-tranne esiste per il codice che viene eseguito quando (e solo quando) l'operazione tentata ha esito positivo. Può essere usato e può essere abusato.
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
Personalmente, mi piace e lo uso quando appropriato. Raggruppa semanticamente le dichiarazioni.
Forse un uso potrebbe essere:
#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()
Forse anche questo ti servirà.
Ho trovato il try: ... else:costrutto utile nella situazione in cui si eseguono query di database e si registrano i risultati di tali query in un database separato dello stesso tipo / sapore. Diciamo che ho molti thread di lavoro che gestiscono tutte le query del database inviate a una coda
#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
Ovviamente se riesci a distinguere tra le possibili eccezioni che potrebbero essere generate, non devi usarlo, ma se il codice che reagisce a un pezzo di codice riuscito potrebbe generare la stessa eccezione del pezzo di successo, e non puoi semplicemente lascia andare la seconda possibile eccezione o ritorna immediatamente in caso di successo (che ucciderebbe il thread nel mio caso), quindi questo è utile.
elseSpesso può esistere un blocco per integrare la funzionalità presente in ogni exceptblocco.
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.
"""
In questo caso, inconsistency_typeè impostato in ogni blocco escluso, in modo tale che il comportamento sia integrato nel caso senza errori in else.
Ovviamente, lo sto descrivendo come un modello che un giorno potrebbe apparire nel tuo codice. In questo caso specifico, è comunque sufficiente impostare inconsistency_typesu 0 prima del tryblocco.
Ecco un altro posto in cui mi piace usare questo modello:
while data in items:
try
data = json.loads(data)
except ValueError as e:
log error
else:
# work on the `data`
continueinvece - il modello "break out early". Ciò consente di eliminare la clausola "else" e il relativo rientro, facilitando la lettura del codice.
Uno degli scenari d'uso a cui riesco a pensare sono le eccezioni imprevedibili, che possono essere eluse se si riprova. Ad esempio, quando le operazioni nel blocco try coinvolgono numeri casuali:
while True:
try:
r = random.random()
some_operation_that_fails_for_specific_r(r)
except Exception:
continue
else:
break
Ma se è possibile prevedere l'eccezione, è sempre necessario scegliere preventivamente la convalida piuttosto che un'eccezione. Tuttavia, non tutto può essere previsto, quindi questo modello di codice ha il suo posto.
breakinterno tryalla fine, che è IMO più pulito, e non ti serve else. Inoltre continuenon è davvero necessario, puoi semplicemente pass.
Ho trovato elseutile per gestire un file di configurazione forse errato:
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
Un'eccezione durante la lettura della lockconfigurazione disabilita il monitoraggio dei blocchi e ValueErrors registra un utile messaggio di avviso.
Supponiamo che la logica di programmazione dipenda dal fatto che un dizionario abbia una voce con una determinata chiave. Puoi testare il risultato dict.get(key)dell'uso di if... else...construct oppure puoi fare:
try:
val = dic[key]
except KeyError:
do_some_stuff()
else:
do_some_stuff_with_val(val)
Vorrei aggiungere un altro caso d'uso che sembra semplice quando si gestiscono sessioni DB:
# 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()
Il else:blocco è confuso e (quasi) inutile. Fa anche parte delle dichiarazioni fore while.
In realtà, anche su una ifdichiarazione, si else:può abusare in modi davvero terribili creando bug che sono molto difficili da trovare.
Considera questo.
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
Pensaci due volte else:. È generalmente un problema. ifEvitatelo se non in una dichiarazione e anche allora considerate di documentare la elsecondizione - per renderla esplicita.
if x > 0: return "yes"e if x <= 0: return "no". Ora una persona viene e cambia una delle condizioni da dire x > 1ma si dimentica di cambiare l'altra. Come è possibile ridurre il numero di bug che verrebbero commessi. if elsele clausole sono a volte molte righe a parte. DRY è una buona pratica, molto più spesso che no, davvero. (scusa per il doppio post).