# Exceptions in Python: try, except, else, finally, raise und eigene Ausnahmen

URL: https://skillaria.top/de/guides/python/isklyucheniya-python

Eine Ausnahme (Exception) in Python ist ein Objekt, das einen Fehler meldet und den normalen Ablauf unterbricht, bis ein try/except-Block sie abfängt; else läuft nur, wenn kein Fehler auftrat, finally immer. Eine Ausnahme lösen Sie selbst mit raise aus, etwa raise ValueError("Betrag negativ"), und eine eigene Ausnahme erstellen Sie als Klasse, die von Exception erbt.

Eine Ausnahme (Exception) in Python ist ein Objekt, das einen Fehler meldet und den normalen Ablauf des Programms unterbricht. Wird sie nicht abgefangen, gibt Python einen Traceback aus und beendet das Programm. Abgefangen werden Ausnahmen mit dem Konstrukt `try/except`, selbst ausgelöst werden sie mit `raise`. Wir sehen uns an, wie das funktioniert, welche Fehler am häufigsten auftreten und wie man die Fehlerbehandlung nicht in das Verstecken von Fehlern verwandelt.

## Was eine Ausnahme in Python ist

Wenn eine Operation nicht ausgeführt werden kann, etwa bei einer Division durch null, beim Zugriff auf einen nicht vorhandenen Schlüssel oder bei der Umwandlung eines Wortes in eine Zahl, erzeugt Python ein Ausnahmeobjekt und „wirft“ es. Die aktuelle Funktion bricht ab, die Ausnahme wandert durch die Aufrufkette nach oben, bis sie auf einen passenden Handler trifft. Findet sich keiner, stürzt das Programm ab.

```
>>> 10 / 0
Traceback (most recent call last):
  File "", line 1, in
ZeroDivisionError: division by zero
```

Übrigens: Auf die häufige Frage, welche Ausnahme bei einer Division durch null in Python auftritt, lautet die Antwort `ZeroDivisionError`. Das gilt für `/`, `//` und `%`.

## Wie man einen Traceback liest

Der Traceback ist ein Bericht darüber, wie das Programm bis zum Fehler gelangt ist. Man liest ihn am besten von unten nach oben. Die letzte Zeile ist das Wichtigste: der Typ der Ausnahme und die Meldung. Darüber steht die Codezeile, in der es passiert ist, und darüber, wer sie aufgerufen hat, bis zum Einstiegspunkt.

```
Traceback (most recent call last):
  File "tracker.py", line 9, in
    total = parse_amount(row)
  File "tracker.py", line 4, in parse_amount
    return float(raw.strip())
           ~~~~~^^^^^^^^^^^^^
ValueError: could not convert string to float: 'hundert'
```

Seit Python 3.11 werden unter der Codezeile Markierungen wie `~~~^^^` gezeigt (PEP 657). Sie zeigen genau an, welcher Ausdruck in der Zeile fehlgeschlagen ist. Das hilft besonders, wenn in einer Zeile mehrere Aufrufe hintereinander stehen.

### Hinweise „Did you mean“

Seit Python 3.10 schlägt die Fehlermeldung bei einem Tippfehler im Namen eine ähnliche Schreibweise vor. In Python 3.15 sind diese Hinweise noch klüger: Der Interpreter kennt Gewohnheiten aus anderen Sprachen. Der Aufruf `list.push` führt zum Vorschlag `append`, `"Kaffee".toUpperCase()` zu `upper`. Der Hinweis berücksichtigt auch verschachtelte Felder: Liegt ein Dictionary im Attribut `totals` eines Objekts und Sie rufen `items()` direkt am Objekt auf, schlägt der Fehler `.totals.items` vor. Ähnliche Namen schlägt jetzt auch `delattr()` vor.

## Typen von Ausnahmen in Python

Es gibt Dutzende eingebaute Ausnahmen, am Anfang begegnen Ihnen aber meist diese:

| Ausnahme | Wann sie auftritt | Beispiel |
| --- | --- | --- |

| `NameError` | Name nicht definiert, meist ein Tippfehler | `print(summe)` |

| `TypeError` | Operation passt nicht zu diesem Typ | `"5" + 3` |

| `ValueError` | Typ stimmt, der Wert nicht | `int("hundert")` |

| `KeyError` | Schlüssel fehlt im Dictionary | `d["missing"]` |

| `IndexError` | Index außerhalb der Liste | `[1, 2][5]` |

| `AttributeError` | Objekt hat keine solche Methode oder kein solches Feld | `[].push(1)` |

| `ZeroDivisionError` | Division durch null | `1 / 0` |

| `FileNotFoundError` | Datei nicht gefunden | `open("nope.txt")` |

Eine Sonderstellung hat `SyntaxError` (und sein Spezialfall `IndentationError`). Er tritt schon beim Einlesen der Datei auf, vor dem Start des Codes, deshalb lässt er sich in derselben Datei nicht mit `try` abfangen.

### Die Hierarchie der Ausnahmen

Ausnahmen sind Klassen und bilden eine Hierarchie. Ganz oben steht `BaseException`. Davon erben `Exception` (fast alle gewöhnlichen Fehler) und einige Systemausnahmen: `KeyboardInterrupt`, `SystemExit`, `GeneratorExit`.

```
BaseException
 ├── KeyboardInterrupt
 ├── SystemExit
 └── Exception
      ├── ArithmeticError
      │    └── ZeroDivisionError
      ├── LookupError
      │    ├── KeyError
      │    └── IndexError
      ├── OSError
      │    └── FileNotFoundError
      ├── TypeError
      └── ValueError
```

Die Hierarchie ist beim Abfangen wichtig: `except LookupError` fängt sowohl `KeyError` als auch `IndexError`. `KeyboardInterrupt` ist kein Fehler im Code. Damit meldet Python, dass der Benutzer Strg+C gedrückt hat. Die Ausnahme erbt bewusst nicht von `Exception`, damit ein `except Exception` das Abbrechen des Programms nicht verhindert.

## Fehlerbehandlung: try/except/else/finally

Code, der fehlschlagen kann, kommt in den Block `try`. Im Block `except` geben Sie den Fehlertyp an und legen fest, was passieren soll. Genau dieser `except`-Block dient in Python der Behandlung von Ausnahmen.

```
def parse_amount(raw):
    try:
        value = float(raw)
    except ValueError as e:
        print(f"Zeile wird übersprungen: {e}")
        return None
    else:
        return value          # wird ausgeführt, wenn kein Fehler auftrat
    finally:
        print("Analyse abgeschlossen")   # wird immer ausgeführt

print(parse_amount("250"))
```

Die Ausgabe lautet hier: zuerst „Analyse abgeschlossen“, dann `250.0`. Ein `return` im Block `else` merkt sich den Wert, danach führt Python auf jeden Fall `finally` aus und gibt erst dann das Ergebnis zurück. Deshalb ist `finally` verlässlich für Aufräumarbeiten, etwa zum Schließen einer Datei oder zum Freigeben einer Verbindung.

Schreiben Sie in `finally` ein eigenes `return`, überschreibt es das vorherige und kann die Ausnahme sogar verschlucken. Seit Python 3.14 gibt es für solchen Code eine `SyntaxWarning` (PEP 765). Schreiben Sie also kein `return` in `finally`.

### Mehrere Ausnahmen gleichzeitig

Sie können mehrere `except`-Blöcke für verschiedene Typen schreiben oder die Typen in einem Tupel aufzählen:

```
try:
    amount = int(data["amount"])
except KeyError:
    amount = 0
except (ValueError, TypeError) as e:
    print("Fehlerhafter Betrag:", e)
    amount = 0
```

Die Blöcke werden von oben nach unten geprüft, der erste passende wird ausgeführt. Deshalb stehen spezifischere Typen vor allgemeinen. Seit Python 3.14 (PEP 758) dürfen mehrere Typen auch ohne Klammern stehen, also `except ValueError, TypeError:`. Mit `as` bleiben die Klammern aber nötig.

### Wie man den Text einer Ausnahme ausgibt und ihren Typ ermittelt

Das Ausnahmeobjekt erhalten Sie über `as e`. `str(e)` liefert die Meldung, `type(e).__name__` den Namen des Typs:

```
try:
    int("hundert")
except ValueError as e:
    print(type(e).__name__)   # ValueError
    print(e)                  # invalid literal for int() with base 10: 'hundert'
```

## Das Modul traceback: vollständiger Fehlertext

Manchmal muss man eine Ausnahme abfangen, den vollständigen Traceback aber trotzdem erhalten, zum Beispiel für ein Protokoll oder einen Bericht. Dafür gibt es das Modul `traceback`:

```
import traceback

try:
    1 / 0
except ZeroDivisionError:
    traceback.print_exc()             # gibt den Traceback auf stderr aus
    text = traceback.format_exc()     # derselbe Text als String
```

Wenn Sie über das Modul `logging` protokollieren, rufen Sie innerhalb von `except` besser `logging.exception("Berechnung fehlgeschlagen")` auf. Der Traceback wird dann automatisch an die Meldung angehängt.

## Python raise: eine Ausnahme selbst auslösen

Die Anweisung `raise` wirft eine Ausnahme von Hand. So teilt eine Funktion mit, dass sie mit diesen Daten nicht arbeiten kann:

```
def add_expense(amount):
    if amount < 0:
        raise ValueError(f"Der Betrag darf nicht negativ sein: {amount}")
    ...

add_expense(-5)
# ValueError: Der Betrag darf nicht negativ sein: -5
```

Python raise ist eine Anweisung und keine Funktion, deshalb stehen hinter dem Wort `raise` keine Klammern als Aufruf. Die Argumente in der Klammer gehen an den Konstruktor der Ausnahme. Wählen Sie den Typ nach seiner Bedeutung: falscher Wert, `ValueError`; falscher Typ, `TypeError`.

### raise ohne Argumente und raise from

Ein nacktes `raise` innerhalb von `except` gibt die aktuelle Ausnahme unverändert weiter. Das braucht man, wenn man etwas protokollieren, die Behandlung aber dem aufrufenden Code überlassen will.

Wenn Sie eine Ausnahme durch eine andere ersetzen, verwenden Sie `raise ... from e`. Die ursprüngliche Ausnahme bleibt dann in der Kette erhalten, und der Traceback zeigt beide:

```
def load_config(path):
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError as e:
        raise RuntimeError(f"Keine Konfiguration {path}") from e
```

### add_note: den Kontext zur Fehlermeldung ergänzen

Seit Python 3.11 (PEP 678) können Sie einer Ausnahme mit der Methode `add_note()` eine Notiz hinzufügen und sie dann weiterwerfen. Der Typ des Fehlers bleibt gleich, die Notiz wird unter der Meldung ausgegeben:

```
def parse(raw, line_no):
    try:
        return float(raw)
    except ValueError as e:
        e.add_note(f"Datei expenses.csv, Zeile {line_no}")
        raise

parse("hundert", 17)
# ValueError: could not convert string to float: 'hundert'
# Datei expenses.csv, Zeile 17
```

## Eine eigene Ausnahme in Python erstellen

Eigene Ausnahmen braucht man für Fehler des Fachgebiets: Budget überschritten, Bestellung bereits bezahlt, Benutzer gesperrt. Dafür genügt eine Klasse, die von `Exception` erbt:

```
class BudgetExceeded(Exception):
    def __init__(self, category, limit, spent):
        super().__init__(f"{category}: Limit {limit}, ausgegeben {spent}")
        self.category = category
        self.limit = limit
        self.spent = spent

def check_budget(category, limit, spent):
    if spent > limit:
        raise BudgetExceeded(category, limit, spent)

try:
    check_budget("Essen", 10000, 12500)
except BudgetExceeded as e:
    print("Warnung:", e)   # Warnung: Essen: Limit 10000, ausgegeben 12500
```

Jetzt fängt der aufrufende Code genau die Überschreitung des Budgets ab und verwechselt sie nicht mit Tippfehlern. In einem großen Projekt lohnt sich eine gemeinsame Basisklasse (`class TrackerError(Exception)`), von der alle eigenen Ausnahmen erben. Dann lassen sie sich mit einem einzigen `except TrackerError` abfangen. Erben Sie von `Exception` und nicht von `BaseException`.

## Ausnahmegruppen und except*

Manchmal treten gleichzeitig mehrere Fehler auf, etwa bei der Prüfung einer ganzen Datei oder bei parallelen Aufgaben mit `asyncio.TaskGroup`. Seit Python 3.11 lassen sie sich in einer `ExceptionGroup` sammeln und mit `except*` nach Typ behandeln:

```
def check_all(rows):
    errors = []
    for r in rows:
        try:
            float(r)
        except ValueError as e:
            errors.append(e)
    if errors:
        raise ExceptionGroup("Fehler in der Datei", errors)

try:
    check_all(["1", "a", "b"])
except* ValueError as eg:
    print(f"Fehlerhafte Zeilen: {len(eg.exceptions)}")   # Fehlerhafte Zeilen: 2
```

## Python try except für alle Fehler: warum das schlecht ist

Die schädlichste Gewohnheit ist, alles abzufangen: `except: pass`. Das Programm „stürzt nicht mehr ab“, meldet Probleme aber auch nicht mehr. Ein nacktes `except` fängt sogar `KeyboardInterrupt` und `SystemExit`, sodass sich das Programm nicht mehr mit Strg+C beenden lässt. `except Exception: pass` ist etwas besser, verschluckt aber trotzdem stillschweigend Tippfehler in Namen und Logikfehler.

So behandelt man Ausnahmen in Python richtig:

- Fangen Sie nur den Typ ab, mit dem Sie umgehen können: `except ValueError` statt allem.
- Halten Sie den Block `try` kurz, nur die Zeilen, die genau so fehlschlagen können.
- Wenn Sie breit abfangen (`except Exception`), dann auf oberster Ebene des Programms und mit Protokollierung des Tracebacks.
- Verwenden Sie Ausnahmen nicht für gewöhnliche Verzweigungen, wo eine Prüfung genügt: In einfachen Fällen ist `dict.get()` besser als `try/except KeyError`.
- Bei Ressourcen nehmen Sie `with`. Es schließt die Datei auch dann, wenn ein Fehler auftritt.

## Fazit

Ausnahmen in Python sind ein normaler Mechanismus, um Fehler zu melden, und kein Grund, sie zu verstecken. Lesen Sie den Traceback von unten nach oben, fangen Sie bestimmte Typen in `try/except` ab, verwenden Sie `else` für den Code „wenn alles geklappt hat“ und `finally` für Aufräumarbeiten. Mit Python raise lösen Sie selbst Fehler aus, `raise from` und `add_note` bewahren den Kontext, und eigene Ausnahmen erben von `Exception`. Im Python-Kurs wird das Thema praktisch behandelt: Der Ausgabentracker stürzt nicht mehr wegen einer fehlerhaften CSV-Zeile ab und meldet eine Budgetüberschreitung über eine eigene Ausnahme.

## FAQ

**Worin unterscheidet sich ein Fehler von einer Ausnahme in Python?**

Im Alltag sind beide Wörter Synonyme. Technisch wird ein SyntaxError schon beim Einlesen des Codes erkannt, die übrigen Fehler treten zur Laufzeit als Ausnahmeobjekte auf, die man abfangen kann.

**Kann die Ausführung nach einer Ausnahme an derselben Stelle weitergehen?**

Nein. Nach dem Abfangen läuft das Programm hinter dem Block try/except weiter, nicht an der Stelle des Fehlers. Wenn Sie jede Zeile einer Datei behandeln wollen, setzen Sie das try in die Schleife.

**Was passiert, wenn eine Ausnahme innerhalb von except auftritt?**

Sie wird weitergereicht, und die ursprüngliche Ausnahme bleibt im Attribut __context__ erhalten. Im Traceback erscheint dann der Satz „During handling of the above exception, another exception occurred“.

**Wie prüfe ich in Tests, dass eine Funktion eine Ausnahme auslöst?**

In pytest nutzt man with pytest.raises(ValueError):, in unittest self.assertRaises(ValueError). Der Test besteht nur, wenn im Block eine Ausnahme des angegebenen Typs auftritt.

**Was ist assert und worin unterscheidet er sich von raise?**

assert bedingung, "Meldung" wirft einen AssertionError, wenn die Bedingung falsch ist. Das ist ein Werkzeug für Prüfungen beim Entwickeln: Mit dem Schalter -O werden asserts abgeschaltet, deshalb prüfen Sie Eingaben von Benutzern besser mit if und raise.


---
Source: Skillaria.Top (https://skillaria.top)
