Fehler sind einfach Werte mit schlechter Laune
Geht in Python etwas schief — durch Null teilen, eine fehlende Datei lesen, eine schlechte Zahl parsen —, erzeugt die Laufzeit ein Ausnahme-Objekt und wickelt den Aufrufstapel ab, bis etwas sie fängt. Fängt nichts sie, wird dein Programm beendet und ein Traceback gedruckt.
Ausnahmen sind nicht per se schlecht. Sie sind, wie Python signalisiert: „ich kann mit dieser Operation nicht weitermachen; das ist der Grund“. Dein Job ist, Fall für Fall zu entscheiden, von welchen du weißt, wie du dich erholst, und welche du eskalieren lassen solltest.
Die Grundform
try:öffnet den Block mit riskantem Code.except ValueError:fängt genau diese Ausnahme, wenn sie imtrygeworfen wird.- Fällt keine Ausnahme, wird das
exceptkomplett übersprungen.
Führ das Snippet mit 42 aus — funktioniert. Mit hello — läuft der Handler.
Gezielt Ausnahmen fangen
Python hat eine Hierarchie von Ausnahmetypen. Ein paar, denen du oft begegnest:
ValueError— ein Wert war irgendwie falsch (int("abc"), Argumente außerhalb des Bereichs).TypeError— der falsche Typ wurde genutzt ("hi" + 3).KeyError— ein Dict-Schlüssel wurde nicht gefunden.IndexError— ein Sequenz-Index lag außerhalb des Bereichs.FileNotFoundError— eine Datei existiert nicht.ZeroDivisionError— durch Null zu teilen versucht.AttributeError— ein Objekt hat das angefragte Attribut nicht.
Fang die konkrete, die du behandeln kannst:
Du kannst mehrere Ausnahmen in einer Klausel als Tupel fangen:
Beachte das as e. Das bindet das Ausnahme-Objekt an e, damit du Nachricht oder Attribute inspizieren kannst.
Vermeide alles zu fangen
Ein nacktes except: fängt wörtlich alles, auch KeyboardInterrupt (dein Strg-C) und System-Exits. Nutz es nicht.
except Exception: ist etwas besser, aber immer noch gefährlich — es schluckt Bugs, die du nicht erwartet hast, und verbirgt die echte Ursache:
# Don't do this without a really good reason.
try:
do_something()
except Exception:
pass
Der richtige Zug ist fast immer, die konkrete Ausnahme zu fangen, von der du weißt, wie du dich erholst. Erreicht eine unerwartete Ausnahme den oberen Rand deines Programms, sagt dir der Traceback genau, was schief ging — das ist ein Feature, kein Bug.
else und finally
Die try-Anweisung hat zwei weitere optionale Klauseln:
elseläuft, wenn dertry-Block ohne Ausnahme beendet wurde.finallyläuft immer — mit oder ohne Ausnahme.
else ist der sauberste Ort für „nur bei Erfolg“-Code — ein try-Block sollte nicht mehr enthalten als den tatsächlich fehleranfälligen Teil. finally ist für Aufräumarbeiten, die auch bei Fehler laufen müssen: Ressource schließen, Lock freigeben, Zustand wiederherstellen.
Selbst Ausnahmen werfen
Mit raise signalisierst du einen Fehler in eigenem Code:
Wähl einen Ausnahmetyp, der zum Problem passt. Greif zuerst zu den Eingebauten — ValueError, TypeError, FileNotFoundError —, bevor du eine eigene Klasse definierst.
Eine eigene Ausnahme definieren
Wenn die Eingebauten die Bedeutung nicht treffen, definier eine eigene Ausnahme:
Erbe von Exception (oder einer spezifischeren Eingebauten) und gib der Klasse einen Docstring. Mehr brauchst du meist nicht. Eigene Ausnahmen erlauben Aufruferinnen, nur den für ihre Domäne relevanten Fehler zu fangen.
raise ... from ...: verkettete Ausnahmen
Wenn eine Ausnahme eine andere auslöst, erhalte die Kette:
Das from e hängt den Originalfehler an. Wird der Traceback gedruckt, zeigt Python beide — den aufgetauchten ConfigError und den FileNotFoundError, der ihn verursacht hat. Diese Spur ist beim Debuggen unbezahlbar.
Context Manager: der sauberere Weg zum Aufräumen
finally ist okay, aber für Ressourcen wie Dateien ist ein Context Manager (was with nutzt) fast immer besser:
# finally version
f = open("data.txt")
try:
data = f.read()
finally:
f.close()
# with version
with open("data.txt") as f:
data = f.read()
Beide sind sicher. Die with-Form ist kürzer und greift automatisch. Greif zu finally nur, wenn du etwas tust, was die Standardbibliothek nicht ohnehin in einen Context Manager wickelt.
Wann du nicht fangen solltest
Eine Ausnahme zu fangen ist eine Entscheidung — du sagst „ich kann das behandeln“. Kannst du es nicht, lass die Ausnahme hochgehen. Code wie dieser ist fast immer ein Fehler:
try:
do_work()
except Exception:
pass # silently ignore everything
Fehler zu unterdrücken macht Bugs unsichtbar. Besser laut abstürzen als in einem inkonsistenten Zustand weiterhumpeln.
Zusammenfassung
try/excepterlaubt dir, Fehler zu behandeln, von denen du dich erholen kannst.- Fang konkrete Ausnahmen, nicht
Exceptionpauschal. raisesignalisiert Fehler in eigenem Code.with-Blöcke ersetzen die meistenfinally-Aufräumarbeiten.- Im Zweifel: lass die Ausnahme durch.
Als Nächstes: eine Tour durch die konkreten Fehler, die Python am häufigsten wirft — KeyError, ValueError, ModuleNotFoundError und ein paar mehr — plus die Debugging-Gewohnheiten, die sie schnell beheben.
Häufig gestellte Fragen
Wie behandle ich Fehler in Python?
Wickle riskanten Code in einen try-Block und fang die konkrete Ausnahme in einem except-Block: try: risky() except ValueError: .... Das optionale else läuft, wenn keine Ausnahme fiel; finally läuft in beiden Fällen, für Aufräumarbeiten.
Soll ich zur Sicherheit Exception fangen?
Nein. Ein nacktes except: oder except Exception: versteckt Bugs, mit denen du nicht gerechnet hast. Fang die konkrete Ausnahme, von der du weißt, wie du dich erholen kannst, und lass alles andere hochgehen, damit du das echte Problem siehst.
Was ist der Unterschied zwischen raise und raise from?
raise NewError(...) wirft eine neue Ausnahme. raise NewError(...) from original hängt die Originalausnahme als Ursache mit an, was Python im Traceback zeigt. Nimm from, wenn ein tieferliegender Fehler einen höheren ausgelöst hat, den du sichtbar machen willst.