5. Ausnahmen
Wir wenden uns nun den Ausnahmen zu.

5.1. Skript [exceptions_01]
Das erste Skript veranschaulicht die Notwendigkeit, Ausnahmen zu behandeln.
Wir lösen absichtlich einen Fehler aus, um zu sehen, was passiert (exceptions-01.py):
Zeile 4 ergibt:
- den Ausnahmetyp: ZeroDivisionError;
- die zugehörige Fehlermeldung: Division durch Null. Sie ist auf Englisch. Das möchten Sie vielleicht ändern.
Eine wichtige Regel lautet, dass wir alles tun müssen, um vom Python-Interpreter ausgelöste Ausnahmen zu vermeiden. Wir müssen Fehler selbst behandeln.
Die Syntax für die Ausnahmebehandlung lautet wie folgt:
try:
actions susceptibles de lancer une exception
except (Ex1, Ex2…) as ex:
actions de gestion de l'exception [ex]
except (Ex11, Ex12…) as ex:
actions de gestion de l'exception [ex]
finally:
actions toujours exécutées qu'il y ait exception ou non
Im try-Block wird die Ausführung der Aktionen beendet, sobald eine Ausnahme (ein Fehler) auftritt. In diesem Fall wird die Ausführung mit den Aktionen einer der except-Klauseln fortgesetzt:
- Zeile 3: Wenn die aufgetretene Ausnahme [ex] zu einem Typ gehört, der zum Tupel (Ex1, Ex2…) gehört oder von einem dieser Typen abgeleitet ist, werden die Aktionen in Zeile 4 ausgeführt;
- Zeile 5: Wenn die Ausnahme nicht von Zeile 3 abgefangen wurde und eine weitere [except]-Klausel vorhanden ist, dann erfolgt derselbe Vorgang. Usw.;
- Es kann so viele [except]-Klauseln geben, wie nötig sind, um die verschiedenen Arten von Ausnahmen zu behandeln, die innerhalb des [try]-Blocks auftreten können;
- Wenn die Ausnahme von keiner der [except]-Klauseln behandelt wurde, wird sie an den aufrufenden Code zurückgegeben. Befindet sich der aufrufende Code selbst innerhalb einer try/except-Struktur, wird die Ausnahme erneut behandelt; andernfalls wird sie weiter die Kette der aufgerufenen Methoden hinaufgegeben. Letztendlich erreicht sie den Python-Interpreter. Der Interpreter beendet dann das laufende Programm und zeigt eine Fehlermeldung des im vorherigen Beispiel gezeigten Typs an. Die Regel lautet daher, dass das Hauptprogramm alle Ausnahmen abfangen muss, die von aufgerufenen Methoden weitergeleitet werden können;
- Zeile 7: Die [finally]-Klausel wird immer ausgeführt, unabhängig davon, ob eine Ausnahme aufgetreten ist (nach dem except) oder nicht (nach dem try). Dies gilt auch dann, wenn eine Ausnahme aufgetreten ist und nicht abgefangen wurde. In diesem Fall wird die [finally]-Klausel ausgeführt, bevor die Ausnahme an den aufrufenden Code zurückgegeben wird;
Eine Ausnahme enthält Informationen über den aufgetretenen Fehler. Diese Informationen können mit der folgenden Syntax abgerufen werden:
except MyException as exception:
[exception] ist die aufgetretene Ausnahme. [exception.args] steht für das Tupel der Parameter der Ausnahme.
Um eine Ausnahme auszulösen, verwenden Sie die Syntax
raise MyException(param1, param2…)
wobei MyException meist eine von der Klasse BaseException abgeleitete Klasse ist. Die an den Klassenkonstruktor übergebenen Parameter stehen der except-Klausel von Ausnahmebehandlungsstrukturen unter Verwendung der Syntax [ex.args] zur Verfügung, wenn [ex] die von der [except]-Klausel abgefangene Ausnahme ist.
Diese Konzepte werden durch das folgende Skript veranschaulicht.
5.2. Skript [exceptions_02]
Das folgende Skript behandelt Fehler explizit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | |
Anmerkungen:
- Zeilen 4–12: Behandlung einer Division durch Null;
- Zeile 8: Wir fangen die aufgetretene Ausnahme ab und geben sie aus;
- Zeile 12: Aufgrund der aufgetretenen Ausnahme hat x in Zeile 7 keinen Wert erhalten und daher seinen Wert nicht geändert;
- Zeilen 14–21: Wir machen dasselbe, fangen jedoch eine übergeordnete Ausnahme vom Typ BaseException ab. Da die Ausnahme ZeroDivisionError eine Unterklasse von BaseException ist, wird sie von der except-Klausel abgefangen;
- Zeilen 23–36: Wir verwenden mehrere except-Klauseln, um mehrere Ausnahmetypen zu behandeln. Es wird nur eine except-Klausel ausgeführt oder gar keine, wenn die Ausnahme keiner except-Klausel entspricht;
- Zeilen 38–50: Wir verfahren genauso, ändern jedoch die Reihenfolge der [except]-Klauseln, um ihre Funktion zu veranschaulichen;
- Zeilen 52–58: Die `except`-Klausel darf keine Argumente haben. In diesem Fall fängt sie alle Ausnahmen ab;
- Zeilen 60–67: Einführung der ValueError-Ausnahme;
- Zeilen 69–75: Wir rufen die von der Ausnahme enthaltenen Informationen ab;
- Zeilen 77–83: Wir zeigen, wie man eine Ausnahme auslöst;
- Zeilen 86–106: Veranschaulichung der Verwendung einer benutzerdefinierten Ausnahmeklasse, MyError. Die Klasse MyError erbt einfach von der Basisklasse BaseException. Sie fügt ihrer Basisklasse nichts hinzu. Sie kann nun jedoch explizit in except-Klauseln benannt werden;
- Zeilen 108–130: veranschaulichen die Verwendung der finally-Klausel;
- Zeilen 132–139: Diese Zeilen zeigen, dass die [except]-Klausel nicht zwingend erforderlich ist;
Die Bildschirmausgabe lautet wie folgt:
5.3. Skript [exceptions_03]
Dieses neue Skript veranschaulicht, wie Ausnahmen in der Kette der aufrufenden Funktionen nach oben weitergeleitet werden:
Anmerkungen:
- Zeilen 25–32: Im Aufruf main → f1 → f2 → f3 (Zeile 30) wird die von f3 ausgelöste MyError-Ausnahme bis zu main weitergeleitet. Sie wird dann von der except-Klausel in Zeile 31 abgefangen;
- Zeilen 61–75: Im Aufruf main → f4 → f5 → f6 (Zeile 62) wird die von f6 ausgelöste MyError-Ausnahme bis zu main weitergeleitet. Sie wird dann von der except-Klausel in Zeile 63 abgefangen. Da sie diesmal die Kette der aufrufenden Funktionen hinaufgeleitet wird, ist die weitergeleitete MyError-Ausnahme selbst in eine andere Ausnahme gekapselt;
- Zeilen 66–75: zeigen, wie man den Ausnahmestapel nachverfolgt;
- Zeile 71: Die Funktion [isinstance(instance, Class)] gibt True zurück, wenn das Objekt [instance] vom Typ [Class] oder einer Unterklasse ist. Hier haben wir die oberste Ausnahme [BaseException] verwendet, wodurch sichergestellt wird, dass wir alle Ausnahmen abfangen;
Die Bildschirmausgabe lautet wie folgt: