Fehler gehören zum Programmieren dazu: Eine Datei fehlt, jemand tippt Text statt einer Zahl, das Netzwerk ist weg. Mit Ausnahmen (Exceptions) behandelst du solche Fälle, ohne dass das ganze Programm abstürzt.
try und except
Der Code, der schiefgehen kann, steht im try-Block. Tritt ein Fehler auf, springt Python in den passenden except-Block:
try:
zahl = int("abc")
print("Zahl:", zahl)
except ValueError:
print("Das war keine gültige Zahl.")
print("Das Programm läuft weiter.")Das war keine gültige Zahl. Das Programm läuft weiter.
Mehrere Fehlerarten
def teile(a, b):
try:
return a / b
except ZeroDivisionError:
print("Durch null teilen geht nicht.")
return None
except TypeError:
print("Bitte Zahlen übergeben.")
return None
print(teile(10, 2))
print(teile(1, 0))
print(teile("x", 2))5.0 Durch null teilen geht nicht. None Bitte Zahlen übergeben. None
Mehrere Typen in einem Zweig fasst du als Tupel zusammen: except (ValueError, TypeError):. Mit as bekommst du das Fehlerobjekt und seine Nachricht:
try:
int("zwölf")
except ValueError as fehler:
print("Fehler:", fehler)Fehler: invalid literal for int() with base 10: 'zwölf'
else und finally
elseläuft, wenn kein Fehler auftrat.finallyläuft immer, zum Beispiel zum Aufräumen.
def lies_zahl(text):
try:
zahl = int(text)
except ValueError:
print(f"'{text}' ist keine Zahl")
else:
print(f"{zahl} ist eine Zahl")
finally:
print("-- geprüft --")
lies_zahl("42")
lies_zahl("x")42 ist eine Zahl -- geprüft -- 'x' ist keine Zahl -- geprüft --
Häufige Ausnahmen
| Ausnahme | Wann sie auftritt |
|---|---|
ValueError | Wert passt nicht, z. B. int("abc") |
TypeError | falscher Typ, z. B. "a" + 1 |
ZeroDivisionError | Division durch null |
IndexError | Listenindex außerhalb |
KeyError | Dictionary-Schlüssel fehlt |
FileNotFoundError | Datei existiert nicht |
AttributeError | Attribut oder Methode fehlt |
NameError | Name nicht definiert |
ImportError | Modul nicht gefunden |
Alle erben von Exception; mit except Exception: fängt man (fast) alles ab. Das solltest du aber nur tun, wenn du den Fehler wirklich behandeln oder protokollieren kannst. Ein leeres except: ist verpönt, weil es auch echte Programmierfehler versteckt.
Eigene Fehler auslösen mit raise
Mit raise löst du selbst eine Ausnahme aus, etwa um ungültige Eingaben zu melden:
def setze_alter(alter):
if alter < 0:
raise ValueError("Alter darf nicht negativ sein")
return alter
try:
setze_alter(-5)
except ValueError as e:
print("Abgelehnt:", e)Abgelehnt: Alter darf nicht negativ sein
Eigene Ausnahme-Klassen
Für größere Programme definierst du eigene Fehlerarten. Sie erben von Exception:
class KontoFehler(Exception):
"""Basisklasse für Probleme mit dem Konto."""
class ZuWenigGuthaben(KontoFehler):
def __init__(self, fehlt):
super().__init__(f"Es fehlen {fehlt} Euro")
self.fehlt = fehlt
def abheben(stand, betrag):
if betrag > stand:
raise ZuWenigGuthaben(betrag - stand)
return stand - betrag
try:
abheben(50, 80)
except KontoFehler as e:
print(type(e).__name__, "-", e)ZuWenigGuthaben - Es fehlen 30 Euro
Lesen mit Sicherheitsnetz
Zum Schluss ein typisches Muster, das Dateien und Eingaben absichert:
def lies_zahlen(dateiname):
try:
with open(dateiname, encoding="utf-8") as f:
return [int(zeile) for zeile in f if zeile.strip()]
except FileNotFoundError:
print("Datei nicht gefunden:", dateiname)
return []
except ValueError:
print("Die Datei enthält etwas, das keine Zahl ist")
return []
print(lies_zahlen("gibt_es_nicht.txt"))Datei nicht gefunden: gibt_es_nicht.txt []
Merke
try:enthält riskanten Code,except Art:behandelt den Fehlerelseläuft ohne Fehler,finallyimmer- Fange möglichst konkrete Ausnahmen, kein leeres
except: raise Art("Text")löst selbst eine Ausnahme aus- Eigene Fehlerarten erben von
Exception except ... as egibt Zugriff auf die Fehlermeldung
Aufgabe
Schreibe eine Funktion sichere_division(a, b), die bei Division durch null None zurückgibt, und eine Eingabe-Schleife, die so lange nach einer ganzen Zahl fragt, bis die Eingabe stimmt.