6. Ausnahmen
Wir wenden uns nun den Ausnahmen zu.
![]() |
Das erste Skript veranschaulicht die Notwendigkeit, Ausnahmen zu behandeln.
# -*- coding=utf-8 -*-
# Es wird ein Fehler ausgelöst
x=4/0
Wir lösen absichtlich einen Fehler aus, um die vom Interpreter ausgegebenen Informationen zu sehen. Die Bildschirmausgabe sieht wie folgt aus:
Zeile 4 gibt Folgendes an:
- den Typ der Ausnahme: ZeroDivisionError;
- die zugehörige Fehlermeldung: „integer division or modulo by zero“. Sie ist auf Englisch. Das möchte man vielleicht ändern.
Die wichtigste Regel beim Programmieren lautet, dass man alles tun muss, um „unvorhergesehene“ Programmabstürze wie den oben genannten zu vermeiden. Selbst im Falle eines Fehlers muss das Programm ordnungsgemäß beendet werden und Informationen über den aufgetretenen Fehler bereitstellen.
Die Syntax für die Ausnahmebehandlung lautet wie folgt:
try:
actions susceptibles de lancer une exception
except [classe d'exception, ...]:
actions de gestion de l'exception
finally:
actions toujours exécutées qu'il y ait exception ou non
Im „try“-Block wird die Ausführung der Aktionen unterbrochen, sobald eine Ausnahme auftritt. In diesem Fall wird die Ausführung mit den Aktionen der „except“-Klausel fortgesetzt.
Die Klausel
except [MyException, ...]:
fängt Ausnahmen vom Typ MyException oder davon abgeleitete Typen ab. Wenn in einem „try“-Block eine Ausnahme auftritt, prüft der Interpreter die mit dem „try“-Block verbundenen „except“-Klauseln in der Reihenfolge, in der sie geschrieben wurden. Er hält bei der ersten „except“-Klausel an, mit der die aufgetretene Ausnahme behandelt werden kann. Findet er keine, wird die Ausnahme an die aufrufende Methode weitergeleitet. Verfügt diese über eine „try/except“-Anweisung, wird die Ausnahme erneut abgefangen; andernfalls wird sie weiter die Kette der aufgerufenen Methoden hinaufgeleitet. Zuletzt gelangt sie zum Python-Interpreter. Dieser beendet dann das ausgeführte Programm und zeigt eine Fehlermeldung an, wie sie im vorherigen Beispiel gezeigt wurde. Die Regel lautet daher, dass das Hauptprogramm alle Ausnahmen abfangen muss, die aus den aufgerufenen Methoden nach oben weitergeleitet werden können.
Eine Ausnahme enthält Informationen über den aufgetretenen Fehler. Diese können mit der folgenden Syntax abgerufen werden:
except MyException as informations:
„informations“ ist ein Tupel, das die Informationen zur Ausnahme enthält.
Die Syntax
except MyException, erreur:
weist der Variablen erreur die mit der Ausnahme verbundene Fehlermeldung zu.
Um eine Ausnahme auszulösen, verwendet man die Syntax
raise MyException(param1, param2, ...)
wobei MyException in den meisten Fällen eine von der Klasse Exception abgeleitete Klasse ist. Die an den Konstruktor der Klasse übergebenen Parameter stehen in der except-Klausel der Ausnahmebehandlungsstrukturen zur Verfügung.
Diese Konzepte werden durch das folgende Skript veranschaulicht.
Das folgende Skript behandelt Fehler explizit:
# -*- coding=utf-8 -*-
i=0
# Es wird ein Fehler ausgelöst und behandelt
x=2
try:
x=4/0
except ZeroDivisionError, erreur:
print ("%s : %s ") % (i, erreur)
# Der Wert von x hat sich nicht geändert
print "x=%s" % (x)
# Wir beginnen von vorne
i+=1
try:
x=4/0
except Exception, erreur:
# Die allgemeinste Ausnahme wird abgefangen
print ("%s : %s ") % (i, erreur)
# Es können mehrere Ausnahmetypen abgefangen werden
i+=1
try:
x=4/0
except ValueError, erreur:
# Diese Ausnahme tritt hier nicht auf
print ("%s : %s ") % (i, erreur)
except Exception, erreur:
# Wir fangen die allgemeinste Ausnahme ab
print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# Man fängt einen bestimmten Typ ab
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
# Wir versuchen es erneut und ändern die Reihenfolge
i+=1
try:
x=4/0
except ValueError, erreur:
# Diese Ausnahme tritt hier nicht auf
print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# Man fängt einen bestimmten Typ ab
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
# Die allgemeinste Ausnahme wird abgefangen
print ("%s : (Exception) %s ") % (i, erreur)
# Eine „except“-Klausel ohne Argumente
i+=1
try:
x=4/0
except:
# Es wird weder auf die Art der Ausnahme noch auf die Fehlermeldung geachtet
print ("%s : il y a eu un probleme ") % (i)
# ein anderer Ausnahmetyp
i+=1
try:
x=int("x")
except ValueError, erreur:
print ("%s : %s ") % (i, erreur)
# Eine Ausnahme enthält Informationen in einem Tupel, auf das das Programm zugreifen kann
i+=1
try:
x=int("x")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# Es können Ausnahmen ausgelöst werden
i+=1
try:
raise ValueError("param1","param2","param3")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# Man kann eigene Ausnahmen definieren
class MyError(Exception):
pass
# Die Ausnahme MyError wird ausgelöst
i+=1
try:
raise MyError("info1","info2", "info3")
except MyError as infos:
print ("%s : %s ") % (i, infos)
# Die Ausnahme MyError wird ausgelöst
i+=1
try:
raise MyError("mon msg d'erreur")
except MyError, erreur:
print ("%s : %s ") % (i, erreur)
# Man kann Objekte beliebigen Typs auslösen
class Objet:
def __init__(self,msg):
self.msg=msg
def __str__(self):
return self.msg
i+=1
try:
raise Objet("pb...")
except Objet as erreur:
print "%s : %s" % (i, erreur)
# Die finally-Klausel wird immer ausgeführt
# unabhängig davon, ob eine Ausnahme auftritt oder nicht
i+=1
x=None
try:
x=1
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
i+=1
x=None
try:
x=2/0
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
Anmerkungen:
- Zeilen 6–9: Eine Division durch Null wird behandelt;
- Zeile 8: Die aufgetretene Ausnahme wird abgefangen;
- Zeile 11: Aufgrund der aufgetretenen Ausnahme hat x keinen Wert erhalten und hat daher seinen Wert nicht geändert;
- Zeilen 15–19: Wir wiederholen den Vorgang, fangen dabei jedoch eine Ausnahme höherer Ebene vom Typ Exception ab. Da die Ausnahme ZeroDivisionError von der Klasse Exception abgeleitet ist, wird sie durch die Klausel except abgefangen;
- Zeilen 23–33: Es werden mehrere Klauseln vom Typ except eingefügt, um verschiedene Ausnahmetypen zu behandeln. Es wird nur eine Klausel vom Typ except ausgeführt oder gar keine, wenn die Ausnahme keiner „except“-Klausel entspricht;
- Zeilen 51–55: Die Klausel except kann ohne Argumente stehen. In diesem Fall stoppt sie alle Ausnahmen;
- Zeilen 59–62: Hier wird die Ausnahme ValueError eingeführt;
- Zeilen 66–69: Die von der Ausnahme übermittelten Informationen werden abgerufen;
- Zeilen 73–76: Hier wird beschrieben, wie eine Ausnahme ausgelöst wird;
- Zeilen 79–84: veranschaulichen die Verwendung einer proprietären Ausnahmeklasse MyError;
- Zeilen 79–80: Die Klasse MyError leitet lediglich von der Basisklasse Exception ab. Sie fügt ihrer Basisklasse nichts hinzu. Nun kann sie jedoch in den „except“-Klauseln explizit genannt werden;
- Zeilen 97–107: veranschaulichen, dass man in Python tatsächlich jede Art von Objekt auslösen kann und nicht nur Objekte, die von der Klasse „Exception“ abgeleitet sind;
- Zeilen 109–127: veranschaulichen die Verwendung der Klausel finally.
Die Bildschirmausgabe lautet wie folgt:
0 : integer division or modulo by zero
x=2
1 : integer division or modulo by zero
2 : (Exception) integer division or modulo by zero
3 : (ZeroDivisionError) integer division or modulo by zero
4 : il y a eu un probleme
5 : invalid literal for int() with base 10: 'x'
6 : invalid literal for int() with base 10: 'x'
7 : ('param1', 'param2', 'param3')
8 : ('info1', 'info2', 'info3')
9 : mon msg d'erreur
10 : pb...
11 : finally x=1
12 : exception
12 : finally x=None
Dieses neue Skript veranschaulicht die Weiterleitung von Ausnahmen in der Kette der aufrufenden Methoden:
# -*- coding=utf-8 -*-
# eine benutzerdefinierte Ausnahme
class MyError(Exception):
pass
# drei Methoden
def f1(x):
# Ausnahmen werden nicht behandelt – sie werden automatisch weitergeleitet
return f2(x)
def f2(y):
# Ausnahmen werden nicht behandelt – sie werden automatisch weitergeleitet
return f3(y)
def f3(z):
if (z % 2) == 0:
# Wenn z gerade ist, wird eine Ausnahme ausgelöst
raise MyError("exception dans f3")
else:
return 2*z
#---------- main
# Die Ausnahmen werden die Kette der aufgerufenen Methoden hinauf weitergeleitet
# bis eine Methode sie abfängt. Hier ist das die Methode „main“
try:
print f1(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
# Eine Methode kann die von ihr weitergeleiteten Ausnahmen erweitern
def f4(x):
try:
return f5(x)
except MyError as infos:
# Die Ausnahme wird ergänzt und anschließend weitergeleitet
raise MyError(infos,"exception dans f4")
def f5(y):
try:
return f6(y)
except MyError as infos:
# Man erweitert die Ausnahme und löst sie dann erneut aus
raise MyError(infos, "exception dans f5")
def f6(z):
if (z % 2) == 0:
# Man löst eine Ausnahme aus
raise MyError("exception dans f6")
else:
return 2*z
#---------- main
try:
print f4(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
Anmerkungen:
- Zeilen 27–30: Beim Aufruf von main --> f1 --> f2 --> f3 (Zeile 28) wird die von f3 ausgelöste Ausnahme MyError bis zu main weitergeleitet. Sie wird dann durch die Klausel except in Zeile 29 verarbeitet;
- Zeilen 55–58: Beim Aufruf von main --> f4 --> f5 --> f6 (Zeile 56) wird die von f6 ausgelöste Ausnahme MyError bis zu main weitergeleitet. Sie wird dann von der Klausel except in Zeile 29 verarbeitet. Bei diesem Durchlaufen der Kette der aufrufenden Methoden wird die Ausnahme MyError durch Informationen ergänzt, die von jeder durchlaufenen Methode hinzugefügt werden.
Die Bildschirmausgabe sieht wie folgt aus:
