Python try-else


582

Qual è l'uso previsto della elseclausola facoltativa della trydichiarazione?


1
La maggior parte delle risposte sembra concentrarsi sul perché non possiamo semplicemente inserire il materiale nella clausola else nella clausola try stessa. La domanda stackoverflow.com/questions/3996329 chiede specificamente perché il codice della clausola else non può andare dopo il blocco try stesso, e quella domanda è supportata da questa, ma non vedo una risposta chiara a quella domanda qui. Sento stackoverflow.com/a/3996378/1503120 che risponde in modo eccellente a questa domanda. Ho anche cercato di chiarire il significato diverso delle varie clausole su stackoverflow.com/a/22579805/1503120 .
Jamadagni,

Volete che si verifichi qualcosa se l'eccezione non si attiva, prima della pulizia finale, che non dovrebbe mai innescare la stessa gestione delle eccezioni.
benjimin,

Risposte:


858

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

  1. la seconda operazione viene eseguita solo se non ci sono eccezioni,
  2. viene eseguito prima del finallyblocco e
  3. qualsiasi IOErrors genera non vengono catturati qui

7
Inoltre, tieni presente che le variabili utilizzate nel blocco try possono essere utilizzate nel blocco else, quindi dovresti sempre considerare l'utilizzo di questa variante se non ti aspetti ulteriori eccezioni nel blocco
else

3
Non importa, perché le variabili con ambito try vengono visualizzate al di fuori del tentativo indipendentemente dal fatto che ci sia o meno un altro.
Reinderien,

36
Non esiste una "variabile con ambito try". In Python, gli ambiti variabili sono stabiliti solo da moduli, funzioni e comprensioni, non da strutture di controllo.
mhsmith l'

9
La clausola else ti consente di scrivere codice che ha senso solo se non viene generata un'eccezione; la clausola tranne può semplicemente passare. Se si inserisce la logica nel blocco try, si rischia di nascondere silenziosamente i bug nel codice. Non schiacciare mai le eccezioni che non ti aspettavi.
Alice Purcell,

9
da questa risposta non è chiaro cosa significhi "cadere dal basso" - non solo ciò accade a causa di un'eccezione, ma anche a causa di un return, continueo break.
Antti Haapala

108

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".


53

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.)


50

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.

In sintesi

L' elseistruzione viene eseguita se non ci sono nessun eccezioni e, se non interrotto da una return, continueo breakdichiarazione.

Le altre risposte mancano quest'ultima parte.

Dai documenti:

La elseclausola opzionale viene eseguita se e quando il controllo scorre fuori dalla fine della tryclausola. *

(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, continueo breakeconomico.

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.

Esempio di differenziazione elserispetto al codice che segue il tryblocco

Se 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!

26

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.


19

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()

9

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.


3
Cosa c'è che non va something = a[2]; print somethingall'interno di try: block?
S.Lott

@ S. Niente di niente, ma cosa succede se qualcuno ti sta inviando un elenco e non vuoi visualizzare i dati se non è abbastanza lungo perché probabilmente è danneggiato?
Sconosciuto

12
S. Lott: "stampare qualcosa" potrebbe sollevare un'eccezione diversa che non si desidera intercettare.
Darius Bacon,

Non vedo la differenza. Se ottengo un'eccezione fuori limite, stampa "fuori limite". Capito. Se ottengo qualche altra eccezione, non è rilevata da questo blocco di codice. Se non ottengo alcuna eccezione, il comportamento è quello di stampare il valore di qualcosa, che è un [2]. Non vedo cosa fa l'altro in questo esempio.
S.Lott

3
Il valore di "qualcosa", una volta stampato, potrebbe aumentare l'errore nel suo metodo __str __ (). Sebbene questo valore sia in realtà solo 2 in questo esempio, potresti anche sottolineare che qui non c'è nemmeno un'eccezione fuori limite.
Darius Bacon,

8

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.


7

Da errori ed eccezioni # Gestione delle eccezioni - docs.python.org

La try ... exceptdichiarazione ha una elseclausola 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.


6

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.


4
"Le eccezioni nella clausola else non sono gestite dalle clausole precedenti tranne." Questa è la parte utile. Grazie.
geowa4,

"La clausola else opzionale viene eseguita se e quando il controllo scorre fuori dalla fine della clausola try" è un'altra differenza, poiché è possibile tornare fuori dal tryblocco.
Tomer W,

4

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.


2

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à.


2

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.


1

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.


1

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`

1
Puoi semplicemente usare continueinvece - il modello "break out early". Ciò consente di eliminare la clausola "else" e il relativo rientro, facilitando la lettura del codice.
malthe,

1

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.


1
Puoi farlo mettendo l' breakinterno tryalla fine, che è IMO più pulito, e non ti serve else. Inoltre continuenon è davvero necessario, puoi semplicemente pass.
Dirbaio,

1

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.


1

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)

-1

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()

-17

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.


6
Non sarei d'accordo con questo. Nel blocco "if-elif", "else" è usato come "default" sarebbe usato nel blocco "case" del linguaggio C. Si consiglia sempre di gestire il caso "predefinito" anche se si ritiene di aver coperto tutti i casi in varie condizioni.
Josip,

1
@Josip: usato come "predefinito" può essere fonte di confusione. Il problema è definire chiaramente la condizione che è questa "impostazione predefinita". Una condizione predefinita mal definita può essere la causa principale del comportamento errato. Altrimenti può essere causa di confusione. Dovrebbe essere pensato con molta attenzione in tutti i casi, non solo provare, per e mentre, ma anche.
S.Lott

5
Bene, il codice sopra è totalmente astratto e non fa nulla di significativo, quindi sì, non c'è da meravigliarsi che sia confuso.
luglio

1
@ S.Lott "Ridurrebbe il problema" - e il mio punto è che questo è falso. Penso che abbiamo solo una vera differenza di opinioni. I programmatori errati trovano sempre il modo di scrivere programmi difettosi. Sempre. I buoni programmatori cercano sempre buone pratiche e possono scrivere un buon codice in quasi tutte le lingue. L'eliminazione di costrutti utili dà solo meno potenza ai buoni programmatori senza aiutare in particolare quelli cattivi in ​​quanto quelli sono in grado di inventare un numero infinito di modi per risolvere le cose.
julx

5
Considera: 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).
julx
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.