2.25. Fehler behandeln

Die meisten Laufzeitprobleme in Python treten als Ausnahmen zutage – eine benannte, strukturierte Möglichkeit, zu melden, dass etwas schiefgelaufen ist. ValueError, TypeError, KeyError, OSError, MemoryError sind allesamt Beispiele; jede ist eine Klasse, und das Auslösen (Raising) einer Ausnahme stoppt den aktuellen Aufruf und sucht im umgebenden Code nach einem Handler.

2.25.1. try / except

Umschließen Sie einen Codeblock mit try, um jede von ihm ausgelöste Ausnahme abzufangen:

try:
    n = int(input_text)
except ValueError:
    n = 0

Wenn die Umwandlung int(...) fehlschlägt, springt die Steuerung zum except-Block, anstatt den Fehler weiter zu propagieren. Wenn input_text ein gültiger Ganzzahl-String war, wird der except-Block übersprungen.

Ein einzelnes try kann mehrere except-Blöcke haben, von denen jeder eine andere Art von Fehler abfängt:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

Python gleicht der Reihe nach ab; der erste, dessen Ausnahmeklasse passt, behandelt das Problem. Das Abfangen von Exception (der Basisklasse für fast alles) behandelt jeden Fehler; reservieren Sie das für die äußerste Schicht eines Programms, wo die Alternative ein Absturz wäre.

Warnung

Ein bloßes except: (keine Klasse nach dem Schlüsselwort) fängt auch KeyboardInterrupt ab – die Ausnahme, die die IDE sendet, wenn Sie ihren Stop-Knopf drücken, um ein laufendes Skript zu unterbrechen. Eine Schleife, die in ein bloßes except: pass gehüllt ist, verschluckt die Unterbrechung und läuft weiter, sodass es keine Möglichkeit mehr gibt, das Skript zu stoppen, außer durch Aus- und Einschalten.

Bevorzugen Sie except Exception: gegenüber dem bloßen except:, wenn Sie wirklich breit abfangen müssen. KeyboardInterrupt erbt von BaseException, nicht von Exception, sodass except Exception: den Stop-Knopf funktionsfähig lässt.

2.25.1.1. Die Ausnahme untersuchen

Um die an eine Ausnahme angehängte Nachricht zu lesen, benennen Sie sie mit as:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

Die mit as gebundene Variable ist nur innerhalb des except-Blocks gültig.

2.25.2. else und finally

Ein try-Block hat zwei optionale Ergänzungen.

else wird nur ausgeführt, wenn der try-Block beendet wurde, ohne eine Ausnahme auszulösen:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

„Was zu tun ist, wenn es funktioniert hat“ in else zu legen, hält den try-Block schmal – nur die Zeile, die fehlschlagen könnte, gehört in try.

finally wird am Ende ausgeführt, egal was passiert – ob der try-Block erfolgreich war, eine Ausnahme auslöste und sie behandelt wurde oder eine Ausnahme auslöste und sie gleich propagiert wird:

try:
    do_work()
finally:
    cleanup()
Flussdiagramm, das die vier möglichen Pfade durch try / except / else / finally zeigt: Erfolg geht zu else und dann finally; eine abgefangene Ausnahme geht zu except und dann finally; eine nicht abgefangene Ausnahme geht zu finally und propagiert dann weiter.

finally wird immer ausgeführt. else wird nur auf dem Pfad ohne Ausnahme ausgeführt.

Für die meisten Belegen/Freigeben-Muster ist ein Kontextmanager einem try / finally-Paar vorzuziehen – die Ressource verwaltet ihre eigene Aufräumarbeit selbst.

2.25.3. Häufige eingebaute Ausnahmen

Eine kurze Liste der Ausnahmen, denen Sie häufig begegnen werden:

  • ValueError – richtiger Typ, falscher Wert (bytes([300]) – 300 ist der richtige Typ, liegt aber außerhalb des gültigen Byte-Bereichs von 0..255).

  • TypeError – ganz falscher Typ (len(42)).

  • KeyError – fehlender Schlüssel in einem dict.

  • IndexError – Index über das Ende einer Sequenz hinaus.

  • AttributeError – Zugriff auf ein Attribut, das nicht existiert ("abc".foo).

  • OSError – ein Dateisystem- oder E/A-Fehler.

  • MemoryError – der Heap ist erschöpft. Auf einer speicherbeschränkten Laufzeitumgebung kann dies während des normalen Betriebs auftreten – nicht nur in pathologischen Fällen.