Skip to content

6. Ausnahmen

Wir werden uns nun mit Ausnahmen befassen.

  

Programm (exceptions_01)

Das erste Skript veranschaulicht die Notwendigkeit, Ausnahmen zu behandeln.

1
2
3
4
#   -*- coding=utf-8 -*-

#  we cause an error
x=4/0

Wir lösen absichtlich einen Fehler aus, um die vom Interpreter ausgegebenen Informationen zu sehen. Die Ausgabe lautet wie folgt:

1
2
3
4
Traceback (most recent call last):
  File "D:\data\istia-1112\python\tutoriel\exceptions_01.py", line 4, in <module>
    x=4/0
ZeroDivisionError: integer division or modulo by zero

Zeile 4 liefert uns:

  • den Ausnahmetyp: ZeroDivisionError;
  • die zugehörige Fehlermeldung: Ganzzahlige Division oder Modulo durch Null. Sie ist auf Englisch. Das möchten Sie vielleicht ändern.

Die grundlegende Regel beim Programmieren lautet, dass wir alles tun müssen, um „unkontrollierte“ Abstü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 beendet, sobald eine Ausnahme auftritt. In diesem Fall wird die Ausführung mit den Aktionen in der except-Klausel fortgesetzt.

Die


except [MyException, ...]:

fängt Ausnahmen vom Typ MyException oder davon abgeleiteten 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, die die aufgetretene Ausnahme behandeln kann. Findet er keine, wird die Ausnahme an die aufrufende Methode zurückgegeben. Verfügt diese Methode über einen try/except-Block, wird die Ausnahme erneut behandelt; andernfalls wird sie weiter in der Kette der aufgerufenen Methoden nach oben weitergeleitet. Schließlich 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.

Eine Ausnahme enthält Informationen über den aufgetretenen Fehler. Diese Informationen können mit der folgenden Syntax abgerufen werden:


except MyException as informations:

information ist ein Tupel, das die Informationen zur Ausnahme enthält.

Die Syntax


except MyException, erreur:

weist der Variablen error die mit der Ausnahme verbundene Fehlermeldung zu.

Um eine Ausnahme auszulösen, verwenden Sie die Syntax


raise MyException(param1, param2, ...)

wobei MyException meist eine von der Klasse Exception abgeleitete Klasse ist. Die an den Klassenkonstruktor übergebenen Parameter stehen der except-Klausel von Strukturen zur Ausnahmebehandlung zur Verfügung.

Diese Konzepte werden durch das folgende Skript veranschaulicht.


Programm (exceptions_02)

Das folgende Skript behandelt Fehler explizit:

#   -*- coding=utf-8 -*-

i=0
#  cause an error and manage it
x=2
try:
    x=4/0
except ZeroDivisionError, erreur:
    print ("%s : %s ") % (i, erreur)
#  the value of x has not changed
print "x=%s" % (x)

#   here we go again
i+=1
try:
    x=4/0
except Exception, erreur:
    #  we intercept the most general exception
    print ("%s : %s ") % (i, erreur)

#   several types of exceptions can be intercepted 
i+=1
try:
    x=4/0
except ValueError, erreur:
    #  this exception does not occur here
    print ("%s : %s ") % (i, erreur)
except Exception, erreur:
    #  we intercept the most general exception
    print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    #   we intercept a specific type
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)

#  start again, changing the order 
i+=1
try:
    x=4/0
except ValueError, erreur:
    #  this exception does not occur here
    print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    #   we intercept a specific type
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
    #  we intercept the most general exception
    print ("%s : (Exception) %s ") % (i, erreur)

#  an except clause with no arguments
i+=1
try:
    x=4/0
except:
    #  we're not interested in the nature of the exception or the error msg
    print ("%s : il y a eu un probleme ") % (i)

#   another type of exception
i+=1
try:
    x=int("x")
except ValueError, erreur:
    print ("%s : %s ") % (i, erreur)

#  an exception transports information in a tuple accessible to the program
i+=1
try:
    x=int("x")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

#   exceptions can be thrown
i+=1
try:
    raise ValueError("param1","param2","param3")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

#   you can create your own exceptions
class MyError(Exception):
    pass

#   throw MyError exception
i+=1
try:
    raise MyError("info1","info2", "info3")
except MyError as infos:
    print ("%s : %s ") % (i, infos)

#   throw MyError exception
i+=1
try:
    raise MyError("mon msg d'erreur")
except MyError, erreur:
    print ("%s : %s ") % (i, erreur)

#   any type of object can be launched
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)

#  the finally clause is always executed
#  whether or not there is an exception
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: Behandlung einer Division durch Null;
  • Zeile 8: Wir fangen genau die aufgetretene Ausnahme ab;
  • Zeile 11: Aufgrund der aufgetretenen Ausnahme hat x keinen Wert erhalten und sich daher nicht verändert;
  • Zeilen 15–19: Wir machen dasselbe, fangen aber eine übergeordnete Ausnahme vom Typ Exception ab. Da die Ausnahme ZeroDivisionError von der Klasse Exception abgeleitet ist, wird sie von der except-Klausel abgefangen;
  • Zeilen 23–33: Wir fügen mehrere except-Klauseln ein, um mehrere Ausnahmetypen zu behandeln. Es wird nur eine except-Klausel ausgeführt oder gar keine, wenn die Ausnahme keiner except-Klausel entspricht;
  • Zeilen 51–55: Die except-Klausel darf keine Argumente haben. In diesem Fall fängt sie alle Ausnahmen ab;
  • Zeilen 59–62: Einführung der ValueError-Ausnahme;
  • Zeilen 66–69: Abrufen der von der Ausnahme enthaltenen Informationen;
  • Zeilen 73–76: Erläuterung, wie eine Ausnahme ausgelöst wird;
  • Zeilen 79–84: Veranschaulichung der Verwendung einer benutzerdefinierten Ausnahmeklasse, MyError;
  • Zeilen 79–80: Die Klasse „MyError“ erbt einfach von der Basisklasse „Exception“. Sie fügt ihrer Basisklasse nichts hinzu. Nun kann sie jedoch in „except“-Klauseln explizit genannt werden;
  • Zeilen 97–107: zeigen, dass man in Python tatsächlich jede Art von Objekt auslösen kann, nicht nur Objekte, die von der Klasse *Exception abgeleitet sind;*
  • Zeilen 109–127: veranschaulichen die Verwendung der finally-Klausel.

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

Programm (exceptions_03)

Dieses neue Skript veranschaulicht, wie Ausnahmen entlang der Kette der aufrufenden Methoden weitergeleitet werden:

#    -*- coding=utf-8 -*-

#    a proprietary exception
class MyError(Exception):
    pass

#    three methods
def f1(x):
    #  exceptions are not handled - they are automatically logged
    return f2(x)

def f2(y):
    #  exceptions are not handled - they are automatically logged
    return f3(y)

def f3(z):
    if (z % 2) == 0:
        #  if z is even, throw an exception 
        raise MyError("exception dans f3")
    else:
        return 2*z

#   ---------- hand

#  exceptions go up the chain of methods called
#  until a method intercepts it. In this case, it will be main
try:
    print f1(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

#  a method can enrich the exceptions it raises
def f4(x):
    try:
        return f5(x)
    except MyError as infos:
        #  we enrich the exception then relaunch it
        raise MyError(infos,"exception dans f4")

def f5(y):
    try:
        return f6(y)
    except MyError as infos:
        #  we enrich the exception then relaunch it
        raise MyError(infos, "exception dans f5")

def f6(z):
    if (z % 2) == 0:
        #    throw an exception
        raise MyError("exception dans f6")
    else:
        return 2*z

#   ---------- hand
try:
    print f4(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

Anmerkungen:

  • Zeilen 27–30: Beim Aufruf main --> f1 --> f2 --> f3 (Zeile 28) wird die von f3 ausgelöste MyError-Ausnahme bis zu main weitergeleitet. Sie wird dann durch die except-Klausel in Zeile 29 abgefangen;
  • Zeilen 55–58: Im Aufruf main → f4 → f5 → f6 (Zeile 56) wird die von f6 ausgelöste MyError-Ausnahme bis nach main weitergeleitet. Sie wird dann von der except-Klausel in Zeile 29 abgefangen. Während sie diesmal die Kette der aufrufenden Methoden hinaufwandert, wird die MyError-Ausnahme um Informationen angereichert, die von jeder Methode auf dem Weg hinzugefügt werden.

Die Bildschirmausgabe lautet wie folgt:

type :<class '__main__.MyError'>, arguments : exception dans f3
type :<class '__main__.MyError'>, arguments : (MyError(MyError('exception dans f6',), 'exception dans f5'), 'exception dans f4')