Python try-else


581

Was ist die beabsichtigte Verwendung der optionalen elseKlausel der tryErklärung?


1
Die meisten Antworten scheinen sich darauf zu konzentrieren, warum wir das Material nicht einfach in die else-Klausel in der try-Klausel selbst einfügen können. In der Frage stackoverflow.com/questions/3996329 wird speziell gefragt, warum der else- Klauselcode nicht nach dem try-Block selbst stehen kann, und diese Frage wird auf diese Frage reduziert, aber ich sehe hier keine eindeutige Antwort auf diese Frage. Ich bin der Meinung, dass stackoverflow.com/a/3996378/1503120 diese Frage hervorragend beantwortet. Ich habe auch versucht, die unterschiedliche Bedeutung der verschiedenen Klauseln unter stackoverflow.com/a/22579805/1503120 zu erläutern .
Jamadagni

Sie möchten, dass etwas passiert, wenn die Ausnahme vor der endgültigen Bereinigung nicht ausgelöst wird, das selbst nie dieselbe Ausnahmebehandlung auslösen soll.
Benjamin

Antworten:


858

Die Anweisungen im elseBlock werden ausgeführt, wenn die Ausführung am Ende von try- abfällt, wenn keine Ausnahme vorliegt. Ehrlich gesagt habe ich nie einen Bedarf gefunden.

Allerdings Behandeln von Ausnahmen Hinweise:

Die Verwendung der else-Klausel ist besser als das Hinzufügen von zusätzlichem Code zur try-Klausel, da dadurch vermieden wird, dass versehentlich eine Ausnahme abgefangen wird, die nicht durch den durch die try ... außer -Anweisung geschützten Code ausgelöst wurde.

Wenn Sie also eine Methode haben, die beispielsweise eine auslösen könnte, IOErrorund Sie Ausnahmen abfangen möchten, die ausgelöst werden, möchten Sie noch etwas tun, wenn die erste Operation erfolgreich ist und Sie keinen IOError abfangen möchten Bei dieser Operation könnten Sie so etwas schreiben:

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

Wenn Sie nur another_operation_that_can_throw_ioerror()nach setzen operation_that_can_throw_ioerror, exceptwürde der Fehler des zweiten Anrufs abfangen. Und wenn Sie es nach dem ganzen tryBlock setzen, wird es immer ausgeführt und erst nach dem finally. Mit elsekönnen Sie sicher gehen

  1. Die zweite Operation wird nur ausgeführt, wenn keine Ausnahme vorliegt.
  2. es wird vor dem finallyBlock ausgeführt, und
  3. Alle IOErrors, die es erhöht, werden hier nicht gefangen

7
Denken Sie auch daran, dass Variablen, die im try-Block verwendet werden, im else-Block verwendet werden können. Sie sollten daher immer in Betracht ziehen, diese Variante zu verwenden, wenn Sie keine weiteren Ausnahmen im else-Block erwarten
WorldSEnder

3
Das spielt keine Rolle, da Variablen mit Try-Scope außerhalb des Try angezeigt werden, unabhängig davon, ob es ein anderes gibt oder nicht.
Reinderien

36
Es gibt keine "Variable mit Versuchsbereich". In Python werden variable Bereiche nur durch Module, Funktionen und Verständnis festgelegt, nicht durch Kontrollstrukturen.
Mhsmith

9
Mit der else-Klausel können Sie Code schreiben, der nur dann sinnvoll ist, wenn keine Ausnahme ausgelöst wurde. Die Ausnahmeklausel kann einfach bestehen. Wenn Sie die Logik in den try-Block einfügen, riskieren Sie, Fehler in Ihrem Code stillschweigend zu verbergen. Zerquetsche niemals Ausnahmen, die du nicht erwartet hast.
Alice Purcell

9
es ist nicht von dieser Antwort klar , was die „Aus dem Boden fällt“ bedeutet - nicht nur dies geschieht aufgrund einer Ausnahme , sondern auch, weil der eines return, continueoder break.
Antti Haapala

108

Es gibt einen großen Grund für die Verwendung else- Stil und Lesbarkeit. Im Allgemeinen ist es eine gute Idee, Code beizubehalten, der Ausnahmen in der Nähe des Codes verursachen kann, der sich mit ihnen befasst. Vergleichen Sie zum Beispiel diese:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

und

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

Der zweite ist gut, wenn der exceptnicht vorzeitig zurückkehren oder die Ausnahme erneut auslösen kann. Wenn möglich hätte ich geschrieben:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

Hinweis: Die Antwort wurde von einem kürzlich veröffentlichten Duplikat hier kopiert , daher all das "AskPassword" -Ding.


53

Eine Verwendung: Testen Sie einen Code, der eine Ausnahme auslösen soll.

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(Dieser Code sollte in der Praxis in einen allgemeineren Test abstrahiert werden.)


50

Python try-else

Was ist die beabsichtigte Verwendung der optionalen elseKlausel der try-Anweisung?

Die beabsichtigte Verwendung besteht darin, einen Kontext zu haben, in dem mehr Code ausgeführt werden kann, wenn es keine Ausnahmen gab, in denen erwartet wurde, dass er verarbeitet wird.

Dieser Kontext vermeidet die versehentliche Behandlung von Fehlern, die Sie nicht erwartet haben.

Aber es ist wichtig , die genauen Bedingungen zu verstehen, die sonst Klausel Sicht verursachen, weil return, continueund breakkann den Steuerfluss zu unterbrechen else.

Zusammenfassend

Die elseAnweisung läuft , wenn es keine Ausnahmen und wenn nicht durch eine unterbrochen return, continueoder breakAussage.

Die anderen Antworten verpassen diesen letzten Teil.

Aus den Dokumenten:

Die optionale elseKlausel wird ausgeführt, wenn die Kontrolle am Ende der tryKlausel abläuft. *

(Fettdruck hinzugefügt.) Und die Fußnote lautet:

* Derzeit Kontrolle „abfließt Ende“ außer im Fall einer Ausnahme oder die Ausführung einer return, continueoder breakAussage.

Es ist mindestens eine vorhergehende Ausnahmeklausel erforderlich ( siehe Grammatik ). Es ist also wirklich nicht "try-else", sondern "try-without-else (-finally)", wobei else(und finally) optional sind.

Das Python-Tutorial erläutert die beabsichtigte Verwendung:

Die Anweisung try ... without enthält eine optionale else-Klausel, die, sofern vorhanden, allen Ausnahmeregelungen folgen muss. Dies ist nützlich für Code, der ausgeführt werden muss, wenn die try-Klausel keine Ausnahme auslöst. Zum Beispiel:

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

Die Verwendung der else-Klausel ist besser als das Hinzufügen von zusätzlichem Code zur try-Klausel, da dadurch vermieden wird, dass versehentlich eine Ausnahme abgefangen wird, die nicht durch den durch die try ... außer -Anweisung geschützten Code ausgelöst wurde.

Beispiel für die Unterscheidung zwischen elseCode und Code nach dem tryBlock

Wenn Sie einen Fehler behandeln, wird der elseBlock nicht ausgeführt. Zum Beispiel:

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!

Und nun,

>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!

26

Try-außer-else eignet sich hervorragend zum Kombinieren des EAFP-Musters mit Enten-Typisierung :

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

Sie könnten sagen, dass dieser naive Code in Ordnung ist:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

Dies ist eine großartige Möglichkeit, um schwerwiegende Fehler in Ihrem Code versehentlich zu verbergen. Ich habe dort eine Bereinigung getippt, aber der AttributeError, der mich wissen lassen würde, wird verschluckt. Schlimmer noch, was wäre, wenn ich es richtig geschrieben hätte, aber die Bereinigungsmethode wurde gelegentlich an einen Benutzertyp übergeben, der ein falsch benanntes Attribut hatte, was dazu führte, dass es auf halbem Weg stillschweigend fehlschlug und eine Datei nicht geschlossen blieb? Viel Glück beim Debuggen.


19

Ich finde es wirklich nützlich, wenn Sie Aufräumarbeiten durchführen müssen, auch wenn es eine Ausnahme gibt:

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

Auch wenn Sie sich momentan keine Verwendung vorstellen können, können Sie darauf wetten, dass es eine Verwendung dafür geben muss. Hier ist ein einfallsloses Beispiel:

Mit else:

a = [1,2,3]
try:
    something = a[2]
except:
    print "out of bounds"
else:
    print something

Ohne else:

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

Hier haben Sie die Variable somethingdefiniert, wenn kein Fehler ausgegeben wird. Sie können dies außerhalb des tryBlocks entfernen , aber dann ist eine unordentliche Erkennung erforderlich, wenn eine Variable definiert ist.


3
Was ist los mit something = a[2]; print somethingdem try: block?
S.Lott

@ S.Lott nichts, aber was ist, wenn Ihnen jemand eine Liste sendet und Sie die Daten nicht anzeigen möchten, wenn sie nicht lang genug sind, weil sie wahrscheinlich beschädigt sind?
Unbekannt

12
S. Lott: "Etwas drucken" könnte eine andere Ausnahme auslösen, die Sie nicht abfangen möchten.
Darius Bacon

Ich sehe den Unterschied nicht. Wenn ich eine Ausnahme außerhalb der Grenzen erhalte, wird "außerhalb der Grenzen" gedruckt. Verstanden. Wenn ich eine andere Ausnahme bekomme, wird sie von diesem Codeblock nicht erfasst. Wenn ich keine Ausnahme bekomme, besteht das Verhalten darin, den Wert von etwas zu drucken, das ein [2] ist. Ich sehe nicht, was der andere in diesem Beispiel tut.
S.Lott

3
Der Wert von 'etwas' kann beim Drucken den Fehler in der Methode __str __ () auslösen. Während dieser Wert in diesem Beispiel tatsächlich nur 2 ist, können Sie genauso gut darauf hinweisen, dass es auch hier keine Ausnahme außerhalb der Grenzen gibt.
Darius Bacon

8

Es gibt ein schönes Beispiel try-elsein PEP 380 . Grundsätzlich kommt es darauf an, in verschiedenen Teilen des Algorithmus unterschiedliche Ausnahmebehandlungen durchzuführen.

Es ist ungefähr so:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

Auf diese Weise können Sie den Code für die Ausnahmebehandlung näher an die Stelle schreiben, an der die Ausnahme auftritt.


7

Aus Fehlern und Ausnahmen # Behandlung von Ausnahmen - docs.python.org

Die try ... exceptAnweisung enthält eine optionale elseKlausel, die, sofern vorhanden, allen außer Klauseln folgen muss. Dies ist nützlich für Code, der ausgeführt werden muss, wenn die try-Klausel keine Ausnahme auslöst. Zum Beispiel:

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

Die Verwendung der else-Klausel ist besser als das Hinzufügen von zusätzlichem Code zur try-Klausel, da dadurch vermieden wird, dass versehentlich eine Ausnahme abgefangen wird, die nicht durch den durch die try ... außer -Anweisung geschützten Code ausgelöst wurde.


6

Wenn man sich die Python-Referenz ansieht, scheint sie elseausgeführt zu werden, trywenn es keine Ausnahme gibt. Die optionale else-Klausel wird ausgeführt, wenn die Steuerung am Ende der try-Klausel abläuft. 2 Ausnahmen in der else-Klausel werden von den vorhergehenden Ausnahmen nicht behandelt.

Das Eintauchen in Python hat ein Beispiel, in dem, wenn ich das richtig verstehe, im tryBlock versucht wird, ein Modul zu importieren. Wenn dies fehlschlägt, erhalten Sie eine Ausnahme und binden die Standardeinstellung. Wenn dies funktioniert, haben Sie die Möglichkeit, in den elseBlock zu gehen und die erforderlichen Elemente zu binden (siehe Link für das Beispiel und die Erklärung).

Wenn Sie versucht haben, im catchBlock zu arbeiten, könnte dies eine weitere Ausnahme auslösen - ich denke, hier ist der elseBlock nützlich.


4
"Ausnahmen in der else-Klausel werden von den vorhergehenden Ausnahmen nicht behandelt." Das ist der nützliche Teil. Vielen Dank.
Geowa4

"Die optionale else-Klausel wird ausgeführt, wenn die Steuerung am Ende der try-Klausel abläuft" ist ein weiterer Unterschied, da Sie aus dem tryBlock zurückkehren können.
Tomer W

4

Das ist es. Der 'else'-Block einer try-exception-Klausel existiert für Code, der ausgeführt wird, wenn (und nur wenn) die versuchte Operation erfolgreich ist. Es kann verwendet und missbraucht werden.

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

Persönlich mag ich es und benutze es, wenn es angebracht ist. Es gruppiert Anweisungen semantisch.


2

Vielleicht könnte eine Verwendung sein:

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

Vielleicht führt dich das auch zu einer Verwendung.


2

Ich habe festgestellt, dass das try: ... else:Konstrukt in der Situation nützlich ist, in der Sie Datenbankabfragen ausführen und die Ergebnisse dieser Abfragen in einer separaten Datenbank mit demselben Geschmack / Typ protokollieren. Angenommen, ich habe viele Arbeitsthreads, die alle Datenbankabfragen verarbeiten, die an eine Warteschlange gesendet werden

#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

Wenn Sie zwischen den möglichen Ausnahmen unterscheiden können, die möglicherweise ausgelöst werden, müssen Sie dies natürlich nicht verwenden. Wenn Code jedoch auf einen erfolgreichen Code reagiert, wird möglicherweise dieselbe Ausnahme wie beim erfolgreichen Teil ausgelöst, und Sie können dies nicht einfach tun Lassen Sie die zweite mögliche Ausnahme los oder kehren Sie sofort nach Erfolg zurück (was in meinem Fall den Thread töten würde), dann ist dies praktisch.


1

Ein elseBlock kann häufig vorhanden sein, um die Funktionalität zu ergänzen, die in jedem exceptBlock auftritt .

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 diesem Fall inconsistency_typewird in jedem Ausnahmeblock gesetzt, so dass das Verhalten im fehlerfreien Fall in ergänzt wird else.

Natürlich beschreibe ich dies als ein Muster, das eines Tages in Ihrem eigenen Code auftauchen kann. In diesem speziellen Fall setzen Sie ohnehin nur inconsistency_typevor dem tryBlock auf 0 .


1

Hier ist ein weiterer Ort, an dem ich dieses Muster verwenden möchte:

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

1
Sie können continuestattdessen einfach das Muster "Früh ausbrechen" verwenden. Auf diese Weise können Sie die "else" -Klausel und ihren Einzug löschen, um den Code leichter lesbar zu machen.
Malthe

1

Eines der Anwendungsszenarien, an die ich denken kann, sind unvorhersehbare Ausnahmen, die umgangen werden können, wenn Sie es erneut versuchen. Wenn die Operationen im try-Block beispielsweise Zufallszahlen enthalten:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

Wenn die Ausnahme jedoch vorhergesagt werden kann, sollten Sie die Validierung immer vor einer Ausnahme auswählen. Es kann jedoch nicht alles vorhergesagt werden, so dass dieses Codemuster seinen Platz hat.


1
Sie können dies tun, indem Sie das breakInnere tryam Ende platzieren, was IMO sauberer ist, und Sie brauchen das nicht else. Auch das continuewird nicht wirklich benötigt, kann man einfach pass.
Dirbaio

1

Ich habe festgestellt, dass es elsenützlich ist, mit einer möglicherweise falschen Konfigurationsdatei umzugehen:

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

Eine Ausnahme beim Lesen der lockKonfiguration deaktiviert die Sperrüberwachung, und ValueErrors protokolliert eine hilfreiche Warnmeldung.


1

Angenommen, Ihre Programmierlogik hängt davon ab, ob ein Wörterbuch einen Eintrag mit einem bestimmten Schlüssel hat. Sie können das Ergebnis der dict.get(key)Verwendung von if... else...Konstrukt testen oder Folgendes tun:

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

-1

Ich würde einen weiteren Anwendungsfall hinzufügen, der bei der Behandlung von DB-Sitzungen unkompliziert erscheint:

    # 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

Der else:Block ist verwirrend und (fast) nutzlos. Es ist auch Teil der forund whileAussagen.

Selbst auf einer ifAussage else:kann das auf wirklich schreckliche Weise missbraucht werden, was zu Fehlern führt, die sehr schwer zu finden sind.

Bedenken Sie.

   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

Überlegen Sie zweimal else:. Es ist im Allgemeinen ein Problem. Vermeiden Sie es, außer in einer ifAnweisung, und dokumentieren Sie auch dann die elseBedingung, um es explizit zu machen.


6
Ich würde diesem nicht zustimmen. Im "if-elif" -Block wird "else" als "Standard" verwendet, der im "case" -Block der C-Sprache verwendet wird. Es wird immer empfohlen, "Standard" -Fälle zu behandeln, auch wenn Sie der Meinung sind, dass Sie alle Fälle unter verschiedenen Bedingungen abgedeckt haben.
Josip

1
@ Josip: Als "Standard" verwendet kann verwirrend sein. Das Problem besteht darin, die Bedingung, die dieser "Standard" ist, klar zu definieren. Eine schlecht definierte Standardbedingung kann die Hauptursache für fehlerhaftes Verhalten sein. Andernfalls kann es zu Verwirrung kommen. Es sollte in jedem Fall sehr sorgfältig durchdacht werden, nicht nur für eine Weile, sondern auch.
S.Lott

5
Nun, der obige Code ist völlig abstrakt und macht nichts Sinnvolles, also ja - kein Wunder, dass er verwirrend ist.
Julx

1
@ S.Lott "Es würde Buggy reduzieren" - und mein Punkt ist, dass dies falsch ist. Ich denke, wir haben nur einen echten Meinungsunterschied. Schlechte Programmierer finden immer Möglichkeiten, fehlerhafte Programme zu schreiben. Immer. Gute Programmierer suchen immer nach guten Praktiken und können guten Code in nahezu jeder Sprache schreiben. Das Eliminieren nützlicher Konstrukte gibt den guten Programmierern nur weniger Macht, während sie den schlechten nicht besonders helfen, da diese in der Lage sind, eine unendliche Anzahl von Möglichkeiten zu erfinden, um Dinge zu ficken.
Julx

5
Betrachten Sie: if x > 0: return "yes"und if x <= 0: return "no". Jetzt kommt eine Person und ändert eine der Bedingungen, um zu sagen x > 1, vergisst aber, die andere zu ändern. Wie reduziert sich die Anzahl der Fehler, die begangen werden würden? if elseKlauseln sind manchmal viele Zeilen voneinander entfernt. DRY ist eine gute Praxis, viel öfter als nicht. (Entschuldigung für den doppelten Beitrag).
Julx
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.