Programmiersprachen Python

Python Exception Chaining (Implicit und Explicit)

In Python können beim Auftreten von Ausnahmen (Exceptions) Fehler an anderer Stelle im Code ausgelöst werden, während die ursprüngliche Ausnahme noch erhalten bleibt. Dies ist besonders nützlich, wenn du eine Ausnahme abfangen möchtest, aber eine neue, spezifischere Ausnahme auslösen musst.


3 Minuten Lesezeit
18 Okt 2024
Python Exception Chaining (Implicit und Explicit)

Was lerne ich in diesem Kurs?

In diesem Tutorial lernst du implizites und explizites Exception Chaining in Python kennen und wie man es benutzt.

Exception Chaining

Exception Chaining in Python ist ein nützliches Werkzeug, um Fehler in einer klaren und nachvollziehbaren Weise zu verketten.

Die Wahl zwischen implizitem und explizitem Chaining hängt davon ab, ob du die ursprüngliche Ausnahme explizit behandeln möchtest oder nicht. In jedem Fall bietet Python mit Exception Chaining eine Möglichkeit, Fehlersituationen transparent zu machen und deren Ursache einfach nachzuvollziehen.

Implizites Exception Chaining

Das implizite Exception Chaining tritt auf, wenn innerhalb eines except-Blocks eine neue Ausnahme ausgelöst wird, ohne explizit die ursprüngliche Ausnahme zu referenzieren. Python verknüpft die ursprüngliche Ausnahme automatisch mit der neuen Ausnahme über das Attribut __context__.

Beispiel 1: Implizites Chaining

try:
    1 / 0  # Löst ZeroDivisionError aus
except ZeroDivisionError:
    raise ValueError("Ein Fehler trat auf.")  # Löst ValueError aus

In diesem Beispiel tritt zuerst eine ZeroDivisionError-Ausnahme auf. Innerhalb des except-Blocks wird jedoch eine neue Ausnahme, ein ValueError, ausgelöst. Python verknüpft den ursprünglichen Fehler mit dem neuen, und die ursprüngliche Ausnahme ist unter dem Attribut __context__ verfügbar.

Wenn du das Programm ausführst, wird der Fehlerverlauf angezeigt, einschließlich des ursprünglichen Fehlers:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "example.py", line 4, in <module>
    raise ValueError("Ein Fehler trat auf.")
ValueError: Ein Fehler trat auf.

Beispiel 2: Implizites Chaining mit mehreren Ausnahmen

try:
    numbers = [1, 2, 3]
    print(numbers[5])  # Löst IndexError aus
except IndexError:
    raise KeyError("Falscher Schlüssel.")

Auch hier tritt zuerst ein IndexError auf, weil auf ein nicht existierendes Element der Liste zugegriffen wird. Danach wird ein KeyError ausgelöst, der mit dem ursprünglichen Fehler verkettet wird:

Traceback (most recent call last):
  File "example.py", line 3, in <module>
    print(numbers[5])
IndexError: list index out of range

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "example.py", line 5, in <module>
    raise KeyError("Falscher Schlüssel.")
KeyError: 'Falscher Schlüssel.'

Explizites Exception Chaining

Beim expliziten Exception Chaining kannst du die ursprüngliche Ausnahme explizit referenzieren, wenn du eine neue Ausnahme auslöst. Das geschieht mit dem Schlüsselwort from. Hierdurch wird die neue Ausnahme mit der alten verknüpft, und du kannst explizit festlegen, welche Ausnahme der Verursacher war.

Beispiel 1: Explizites Exception Chaining

try:
    open("nicht_existierende_datei.txt")  # Löst FileNotFoundError aus
except FileNotFoundError as e:
    raise RuntimeError("Fehler beim Öffnen der Datei.") from e

In diesem Beispiel tritt ein FileNotFoundError auf, weil die Datei nicht existiert. Wir fangen diese Ausnahme ab und lösen explizit eine neue RuntimeError-Ausnahme aus, die mit der ursprünglichen Ausnahme FileNotFoundError verknüpft ist:

Traceback (most recent call last):
  File "example.py", line 2, in <module>
    open("nicht_existierende_datei.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'nicht_existierende_datei.txt'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "example.py", line 4, in <module>
    raise RuntimeError("Fehler beim Öffnen der Datei.") from e
RuntimeError: Fehler beim Öffnen der Datei.

Hier wird explizit der FileNotFoundError als Ursache für den RuntimeError angegeben.


Beispiel 2: Exception Chaining mit mehreren Ebenen

Manchmal kann eine Ausnahme mehrfach verkettet werden, z. B. wenn mehrere Ausnahmen in verschiedenen Ebenen des Programms auftreten. Das folgende Beispiel zeigt, wie man eine Kette von Ausnahmen erstellt:

try:
    raise ValueError("Ursprünglicher Fehler.")
except ValueError as e:
    try:
        raise TypeError("Ein weiterer Fehler.") from e
    except TypeError as te:
        raise RuntimeError("Endgültiger Fehler.") from te

Hier tritt eine Kette von Ausnahmen auf: Zuerst ValueError, dann TypeError, und schließlich RuntimeError, alle miteinander verkettet.

Traceback (most recent call last):
  File "example.py", line 4, in <module>
    raise ValueError("Ursprünglicher Fehler.")
ValueError: Ursprünglicher Fehler.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "example.py", line 7, in <module>
    raise TypeError("Ein weiterer Fehler.") from e
TypeError: Ein weiterer Fehler.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "example.py", line 9, in <module>
    raise RuntimeError("Endgültiger Fehler.") from te
RuntimeError: Endgültiger Fehler.

context und cause

In Python gibt es zwei Attribute, die sich mit Exception Chaining beschäftigen: __context__ und __cause__. Beide spielen eine Rolle beim Verketten von Ausnahmen, aber sie werden in unterschiedlichen Situationen verwendet.

Hier sind die Unterschiede:

__context__:

  • Wird gesetzt, wenn eine Ausnahme innerhalb eines except-Blocks auftritt (implizites Exception Chaining).
  • Python füllt __context__ automatisch aus, wenn du eine neue Ausnahme auslöst, während du eine andere Ausnahme abfängst.
  • __context__ zeigt auf die ursprüngliche Ausnahme, die nicht explizit angegeben wurde.

__cause__:

  • Wird verwendet, wenn explizites Exception Chaining mit raise ... from ... auftritt.
  • Wenn du raise NewException from OldException schreibst, wird die alte Ausnahme (OldException) in das Attribut __cause__ der neuen Ausnahme (NewException) gespeichert.
  • __cause__ hebt die Verkettung explizit hervor und gibt dir mehr Kontrolle über die Ausnahmeursache.

Beispiel für __context__

Hier wird gezeigt, wie __context__ in einem Fall von implizitem Exception Chaining verwendet wird:

try:
    try:
        1 / 0  # Löst ZeroDivisionError aus
    except ZeroDivisionError:
        raise ValueError("Fehler in der Berechnung")  # Neue Ausnahme wird ausgelöst
except ValueError as e:
    print("ValueError aufgetreten:", e)
    if e.__context__:
        print("Ursprüngliche Ausnahme (__context__):", e.__context__)

Erklärung:

  • ZeroDivisionError wird im inneren try-Block ausgelöst.
  • Im except-Block wird eine neue Ausnahme ValueError ausgelöst.
  • Python verknüpft die neue Ausnahme implizit mit der ursprünglichen Ausnahme und speichert den ZeroDivisionError in __context__.

Ausgabe:

ValueError aufgetreten: Fehler in der Berechnung
Ursprüngliche Ausnahme (__context__): division by zero

Beispiel für __cause__

Im folgenden Beispiel verwenden wir explizites Exception Chaining mit raise ... from ...:

try:
    try:
        open("nicht_existierende_datei.txt")  # Löst FileNotFoundError aus
    except FileNotFoundError as e:
        raise RuntimeError("Fehler beim Öffnen der Datei") from e
except RuntimeError as e:
    print("RuntimeError aufgetreten:", e)
    if e.__cause__:
        print("Ursprüngliche Ausnahme (__cause__):", e.__cause__)

Erklärung:

  • FileNotFoundError wird ausgelöst, da die Datei nicht existiert.
  • Eine neue Ausnahme RuntimeError wird explizit mit from verknüpft.
  • Die ursprüngliche Ausnahme FileNotFoundError wird in __cause__ gespeichert.

Ausgabe:

RuntimeError aufgetreten: Fehler beim Öffnen der Datei
Ursprüngliche Ausnahme (__cause__): [Errno 2] No such file or directory: 'nicht_existierende_datei.txt'

Online- und Präsenzkurse zum Thema

Finden Sie interessante und zum Thema passende Kurse

Python Einführungskurs

Beginnen Sie Ihre Programmierkarriere mit unserem Python-Einführungskurs. Erlernen Sie grundlegende Konzepte wie Variablen, Schleifen und Datenstrukturen und bauen Sie eine solide Basis in Python auf. Durch praxisnahe Projekte und kompetente Anleitung entwickeln Sie wertvolle Programmierfähigkeiten.

5 Tage Vollzeit auch als Feierabendkurs Online

Nächster Termin: 20. Januar 2025
Preis pro Person: 1600,00 EUR

Rabattaktion: 3 für den Preis von 2!

Schulung Python für Fortgeschrittene

Diese Schulung „Python für Fortgeschrittene“ vertieft fortgeschrittene Programmiertechniken in Python, wie objektorientierte Programmierung, funktionale Programmierung und Persistenz. Die Teilnehmer lernen, wie sie komplexe Designs und Muster in Python umsetzen, effiziente Datenverarbeitung durchführen und wissenschaftliche Python-Bibliotheken nutzen können. Die Schulung bietet zudem praxisnahe Einblicke in Datenbanken, Metaklassen, Caching und maschinelles Lernen mit scikit-learn.

5 Tage Vollzeit Online

Nächster Termin: 13. Januar 2025
Preis pro Person: 1900,00 EUR

Rabattaktion: 3 für den Preis von 2!

Data Science mit Python

In diesem dreitägigen Data Science Seminar mit Python lernen Teilnehmer, wie sie Daten effizient mit Python-Datenstrukturen verarbeiten, analysieren und visualisieren. Die Schulung umfasst die Arbeit mit NumPy und Pandas sowie die Erstellung von aussagekräftigen Diagrammen mit Matplotlib.

3 Tage Vollzeit Online

Nächster Termin: noch kein Datum bekannt
Preis pro Person: 1200,00 EUR

Rabattaktion: 3 für den Preis von 2!