Exceptions

In diesem Abschnitt geht es um Ausnahmen, d.h. um Sprachfunktionen, die speziell ungewöhnliche Umstände während der Ausführung eines Programms behandeln. Die häufigste Ausnahme ist die Behandlung von Fehlern, aber sie können auch für viele andere Zwecke effektiv eingesetzt werden. Python bietet einen umfassenden Satz von Ausnahmen, und ihr könnt neue Ausnahmen für eure eigenen Zwecke definieren.

Der gesamte Exception-Mechanismus in Python ist objektorientiert: Eine Exception ist ein Objekt, das automatisch von Python-Funktionen mit einer raise-Anweisung erzeugt wird. Diese raise-Anweisung veranlasst die Ausführung des Python-Programms auf eine andere Art und Weise, als üblicherweise vorgesehen: Die aktuelle Aufrufkette wird nach einem Handler durchsucht, der die erzeugte Ausnahme behandeln kann. Wenn ein solcher Handler gefunden wird, wird er aufgerufen und kann auf das Ausnahmeobjekt zugreifen, um weitere Informationen zu erhalten. Wird kein geeigneter Exception-Handler gefunden, bricht das Programm mit einer Fehlermeldung ab.

Bemerkung

Die Art und Weise, wie Python Fehlersituationen im Allgemeinen behandelt, unterscheidet sich von manch anderen Sprachen, z.B. Java. Diese Sprachen prüfen mögliche Fehler so weit wie möglich, bevor sie auftreten, da die Behandlung von Exceptions nach ihrem Auftreten kostspielig ist. Dies wird manchmal als LBYL-Ansatz bezeichnet.

Bei Python hingegen verlässt man sich eher auf Exceptions, um Fehler zu behandeln, nachdem sie aufgetreten sind. Obwohl dieses Vertrauen riskant erscheinen mag, ist der Code weniger schwerfällig und leichter zu lesen, wenn Exceptions richtig eingesetzt werden, und Fehler werden nur dann behandelt, wenn sie auftreten. Diese pythonische Herangehensweise zur Behandlung von Fehlern wird oft als EAFP beschrieben.

Es ist möglich, verschiedene Arten von Ausnahmen zu erzeugen, um die tatsächliche Ursache des gemeldeten Fehlers oder außergewöhnlichen Umstandes zu reflektieren. Eine Übersicht über die Klassenhierarchie eingebauter Exceptions erhaltet ihr unter Exception hierarchy in der Python-Dokumentation. Jeder Ausnahmetyp ist eine Python-Klasse, die von ihrem übergeordneten Exception-Typ erbt. So ist z.B. ein ZeroDivisionError durch Vererbung auch ein ArithmeticError, eine Exception und auch eine BaseException. Diese Hierarchie ist gewollt: Die meisten Ausnahmen erben von Exception, und es wird dringend empfohlen, dass alle benutzerdefinierten Ausnahmen auch die Unterklasse von Exception und nicht von BaseException bilden:

1class EmptyFileError(Exception):
2    pass

Dies definiert ihr euren eigenen Ausnahmetyp, der vom Basistyp Exception erbt.

5filenames = ["myFile1.py", "nonExistent.py", "emptyFile.py", "myFile2.py"]

Eine Liste unterschiedlicher Datei-Arten wird definiert.

Schließlich werden Ausnahmen oder Fehler mit Hilfe der zusammengesetzten Anweisung try-except-else-finally abgefangen und behandelt. Jede Ausnahme, die nicht abgefangen wird, führt zur Beendigung des Programms.

 7for file in filenames:
 8    try:
 9        f = open(file, "r")
10        line = f.readline()
11        if line == "":
12            raise EmptyFileError(f"{file} is empty")
13    except OSError as error:
14        print(f"Cannot open file {file}: {error.strerror}")
15    except EmptyFileError as error:
16        print(error)
17    else:
18        print(f"{file}: {f.readline()}")
19    finally:
20        print("File", file, "processed")
21        f.close()
Zeile 7

Wenn während der Ausführung der Anweisungen im try-Block ein OSError oder EmptyFileError auftritt, wird der zugehörige except-Block ausgeführt.

Zeile 9

Hier könnte ein OSError ausgelöst werden.

Zeile 12

Hier löst ihr den EmptyFileError aus.

Zeile 17

Die else-Klausel ist optional; sie wird ausgeführt, wenn im try-Block keine Ausnahme auftritt.

Bemerkung

In diesem Beispiel hätte stattdessen auch continue-Anweisungen in den except-Blöcken verwendet werden können.

Zeile 19

Die finally-Klausel ist optional; sie wird am Ende des Blocks ausgeführt, unabhängig davon, ob eine Ausnahme ausgelöst wurde oder nicht.

Checks

  • Schreibt Code, der zwei Zahlen erhält und die erste Zahl durch die zweite dividiert. Prüft, ob der ZeroDivisionError auftritt, wenn die zweite Zahl 0 ist, und fangt diese ab.

  • Wenn MyError von Exception erbt, was ist dann der Unterschied zwischen except Exception as e und except MyError as e?

  • Schreibt ein einfaches Programm, das eine Zahl erhält und dann die Anweisung assert() verwendet, um eine Exception auszulösen, wenn die Zahl 0 ist.

  • Schreibt eine benutzerdefinierte Ausnahme Outliers, die eine Exception auslöst, wenn die Variable x größer oder kleiner als 3 ist?

  • Handelt es sich bei der Überprüfung, ob ein Objekt eine Liste ist (Check: list) um eine Programmierung im Stil von LBYL oder EAFP?