Qual è l'uso previsto della else
clausola facoltativa della try
dichiarazione?
Qual è l'uso previsto della else
clausola facoltativa della try
dichiarazione?
Risposte:
Le istruzioni nel else
blocco 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 IOError
e 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
, except
catturerebbe gli errori della seconda chiamata. E se lo metti dopo l'intero try
blocco, verrà sempre eseguito e non fino a dopo il finally
. Il else
consente di assicurarsi
finally
blocco eIOError
s genera non vengono catturati quireturn
, continue
o 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 except
non è 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
else
clausola 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
, continue
e break
in grado di interrompere il flusso di controllo a else
.
L' else
istruzione viene eseguita se non ci sono nessun eccezioni e, se non interrotto da una return
, continue
o break
dichiarazione.
La
else
clausola opzionale viene eseguita se e quando il controllo scorre fuori dalla fine dellatry
clausola. *
(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
,continue
obreak
economico.
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.
else
rispetto al codice che segue il try
bloccoSe si gestisce un errore, il else
blocco 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 something
definita se non viene generato alcun errore. Puoi rimuoverlo al di fuori del try
blocco, ma richiede una certa confusione se viene definita una variabile.
something = a[2]; print something
all'interno di try: block?
C'è un bell'esempio di try-else
in 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 ... except
dichiarazione ha unaelse
clausola 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 else
venga eseguito dopo che try
non 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 try
blocco 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 else
blocco e associare ciò che è richiesto (vedi link per l'esempio e la spiegazione).
Se hai provato a fare un lavoro in catch
blocco, potrebbe generare un'altra eccezione: immagino che sia qui che il else
blocco è utile.
try
blocco.
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.
else
Spesso può esistere un blocco per integrare la funzionalità presente in ogni except
blocco.
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_type
su 0 prima del try
blocco.
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`
continue
invece - 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.
break
interno try
alla fine, che è IMO più pulito, e non ti serve else
. Inoltre continue
non è davvero necessario, puoi semplicemente pass
.
Ho trovato else
utile 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 lock
configurazione 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 for
e while
.
In realtà, anche su una if
dichiarazione, 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. if
Evitatelo se non in una dichiarazione e anche allora considerate di documentare la else
condizione - 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 > 1
ma si dimentica di cambiare l'altra. Come è possibile ridurre il numero di bug che verrebbero commessi. if else
le clausole sono a volte molte righe a parte. DRY è una buona pratica, molto più spesso che no, davvero. (scusa per il doppio post).