Ausnahmen#

  • (brit flag exceptions)

Exceptions (Ausnahmen) treten auf, wenn außergewöhnliche Situationen in Ihrem Programm auftreten. Was wäre zum Beispiel, wenn Sie eine Datei lesen möchten und die Datei nicht existiert? Oder was, wenn Sie sie versehentlich gelöscht haben, während das Programm lief? Solche Situationen werden mit exceptions behandelt.

Ebenso: Was wäre, wenn Ihr Programm ungültige Anweisungen enthielte? Python meldet dies und teilt Ihnen mit, dass ein Fehler vorliegt. (Python raises an error )

Fehler#

  • (brit flag errors)

Betrachten wir einen einfachen Aufruf der print-Funktion. Was wäre, wenn wir print fälschlicherweise als Print schreiben? Beachten Sie die Großschreibung. In diesem Fall wirft Python einen Syntaxfehler.

>>> Print("Hallo Welt")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined
>>> print("Hallo Welt")
Hallo World

Beachten Sie, dass ein NameError ausgelöst wird und auch der Ort, an dem der Fehler erkannt wurde, angezeigt wird. Dies ist die Aufgabe eines Fehlerbehandlers (error_handlers) für diesen Fehler.


Exceptions#

Wir werden versuchen (try), eine Eingabe vom Benutzer zu lesen. Geben Sie die folgende Zeile ein und drücken Sie die Eingabetaste. Wenn Ihr Computer Sie zur Eingabe auffordert, drücken Sie stattdessen [Strg+D] auf einem Mac bzw. Linux-Rechner oder [Strg+Z] unter Windows und schauen Sie, was passiert. (Wenn Sie Windows verwenden und keine der Optionen funktioniert, können Sie [Strg+C] in der Eingabeaufforderung versuchen, um stattdessen einen KeyboardInterrupt-Fehler zu erzeugen.)

>>> s = input('Schreib was --> ')
Enter something --> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
EOFError

Python löst einen Fehler namens EOFError (End_of_file) aus, was im Grunde bedeutet, dass es ein Dateiende-Symbol (das durch [Strg+D] dargestellt wird) gefunden hat, als es nicht damit gerechnet hat.


Behandlung von Ausnahmen#

  • (brit flag handling Exceptions)

Wir können Exceptions mit der try...except-Anweisung behandeln. Wir setzen unsere üblichen Anweisungen in den try-Block und alle unsere Fehlerbehandler in den except-Block.

Beispiel exceptions_handle_de.py

Quellcode
1try:
2    text = input('Schreibe etwas --> ')
3except EOFError:
4    print('Warum plötzlich ein EOF (end of file) ?')
5except KeyboardInterrupt:
6    print('Vorgang wurde abgebrochen.')
7else:
8    print('Du hast geschrieben: {}'.format(text))

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe:
$ python3 exceptions_handle_de.py
# drücke STRG + d
Schreibe etwas --> Warum plötzlich ein EOF (end of file) ?
# drücke STRG + c
horst@BigBlackTower:~/code/byte-of-python_deutsch_horst/programs$ python3 exceptions_handle_de.py 
Schreibe etwas --> ^CVorgang wurde abgebrochen.
# 
horst@BigBlackTower:~/code/byte-of-python_deutsch_horst/programs$ python3 exceptions_handle_de.py 
Schreibe etwas --> Keine Exceptions
Du hast geschrieben: Keine Exceptions

Wie es funktioniert

Wir platzieren alle Anweisungen, die Ausnahmen/Fehler auslösen könnten, innerhalb des try-Blocks und definieren anschließend Behandler für die entsprechenden Fehler/Ausnahmen in der except-Klausel bzw. im except-Block. Die except-Klausel kann entweder einen einzelnen angegebenen Fehler bzw. eine einzelne Ausnahme oder eine in Klammern angegebene Liste von Fehlern/Ausnahmen behandeln. Wenn keine Namen von Fehlern oder Ausnahmen angegeben werden, behandelt sie alle Fehler und Ausnahmen.

Beachten Sie, dass jeder try-Klausel mindestens eine except-Klausel zugeordnet sein muss. Andernfalls stellt sich die Frage, welchen Zweck ein try-Block überhaupt erfüllen soll.

Falls ein Fehler oder eine Ausnahme nicht behandelt wird, wird der Standardbehandler von Python aufgerufen, der lediglich die Ausführung des Programms beendet und eine Fehlermeldung ausgibt. Dies haben wir oben bereits in Aktion gesehen.

Sie können außerdem eine else-Klausel mit einem try...except-Block kombinieren. Die else-Klausel wird ausgeführt, wenn keine Ausnahme auftritt.

Im nächsten Beispiel werden wir außerdem sehen, wie das Ausnahmeobjekt abgerufen werden kann, sodass zusätzliche Informationen daraus gewonnen werden können.

Ausnahmen auslösen#

(brit flag raising exceptions)

Sie können Ausnahmen mithilfe der Anweisung raise auslösen, indem Sie den Namen des Fehlers/der Ausnahme sowie das Ausnahmeobjekt angeben, das geworfen werden soll.

Der Fehler bzw. die Exception, die Sie auslösen können, muss eine Klasse sein, die direkt oder indirekt von der Klasse Exception abgeleitet ist.

Beispiel exceptions_raise_de.py

Quellcode
 1class ZuKurzeEingabetException(Exception):
 2    '''Eine user-definierte Exception Klasse'''
 3    def __init__(self, länge, minimum):
 4        Exception.__init__(self)
 5        self.länge = länge
 6        self.minimum = minimum
 7
 8try:
 9    text = input('Schreib etwas --> ')
10    if len(text) < 3:
11        raise ZuKurzeEingabetException(len(text), 3)
12    # weitere Programmierzeilen .... 
13except EOFError:
14    print('Warum End of File (EoF) ?')
15except ZuKurzeEingabetException as ex:
16    print(('ZuKurzeEingabeException: Die Eingabe war: ' +
17           '{0} Zeichen lang, sollte aber mindestens {1} Zeichen haben')
18          .format(ex.länge, ex.minimum))
19else:
20    print('No exception was raised.')

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Wie es funktioniert

Hier erstellen wir unseren eigenen Ausnahmetyp. Dieser neue Ausnahmetyp heißt ZuKurzeEingabetException. Er besitzt zwei Felder – länge, welches die Länge der gegebenen Eingabe darstellt, und minimum, welches die minimale Länge angibt, die das Programm erwartet hat.

In der except-Klausel geben wir die Fehlerklasse an, die mithilfe von as unter einem Variablennamen gespeichert wird, welcher das entsprechende Fehler-/Ausnahmeobjekt enthält. Dies ist analog zu Parametern und Argumenten bei einem Funktionsaufruf. Innerhalb dieser speziellen except-Klausel verwenden wir die Felder länge und minimum des Ausnahmeobjekts, um dem Benutzer eine geeignete Meldung auszugeben.

Try … Finally#

  • (brit flag try: versuchen, probieren)

  • (brit flag finally: abschließend, schlussendlich)

Angenommen, Sie lesen in Ihrem Programm eine Datei. Wie stellen Sie sicher, dass das Dateiobjekt ordnungsgemäß geschlossen wird – unabhängig davon, ob eine Ausnahme ausgelöst wurde oder nicht? Dies kann mithilfe des finally-Blocks erreicht werden.

Um zu funktionieren, braucht das folgende Program eine Datei namens poem.txt im selben Verzeichnis. Es ist nicht so wichtig, was genau in poem.txt drinsteht, so lange es mehrere Textzeilen sind.

Beispiel exceptions_finally_de.py

Quellcode
 1import sys
 2import time
 3
 4f = None   # f für file
 5try:
 6    f = open("poem.txt")
 7    # Textdatei öffnen und Inhalt lesen
 8    while True:
 9        zeile  = f.readline()
10        if len(zeile) == 0:
11            break
12        print(zeile, end='')
13        sys.stdout.flush()
14        print("Drücke jetzt STRG+c")
15        # Sicherstellen daß es eine Weile läuft 
16        time.sleep(2) # 2 Sekunden nichts tun
17except IOError:
18    print("Ich konnte die Datei poem.txt nicht finden")
19except KeyboardInterrupt:
20    print("!! Lesevorgang wurde unterbochen mit Tastatur")
21finally:
22    if f:
23        f.close()
24    print("(Aufräumen: Datei wurde geschlossen)")

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
$ python3 exceptions_finally_de.py
Programming is fun
Drücke jetzt STRG+c
When the work is done
Drücke jetzt STRG+c
if you wanna make your work also fun:
Drücke jetzt STRG+c
^C!! Lesevorgang wurde unterbochen mit Tastatur
(Aufräumen: Datei wurde geschlossen)

Wie es funktioniert

Wir führen die üblichen Datei-Leseoperationen durch, haben jedoch zusätzlich künstlich eine Pause von 2 Sekunden nach der Ausgabe jeder Zeile mithilfe der Funktion time.sleep eingefügt, damit das Programm langsam ausgeführt wird (Python ist von Natur aus sehr schnell). Während das Programm noch läuft, drücken Sie ctrl + c, um das Programm zu unterbrechen bzw. abzubrechen.

Beachten Sie, dass die Exception KeyboardInterrupt ausgelöst wird und das Programm beendet wird. Bevor das Programm jedoch beendet wird, wird die finally-Klausel ausgeführt und das Dateiobjekt wird stets geschlossen.

Beachten Sie außerdem, dass eine Variable mit dem Wert 0 oder None oder eine Variable, die eine leere Sequenz oder Sammlung enthält, von Python als False betrachtet wird. Deshalb können wir im obigen Code if f: verwenden.

Beachten Sie ebenfalls, dass wir nach print den Aufruf sys.stdout.flush() verwenden, damit die Ausgabe sofort auf dem Bildschirm erscheint.

Das with statement#

  • (brit flag with: mit)

Das Erwerben einer Ressource innerhalb des try-Blocks und das anschließende Freigeben der Ressource im finally-Block ist ein häufig auftretendes Muster. Daher existiert außerdem die Anweisung with, die dies auf elegante Weise ermöglicht:

Das folgende Programm benötigt ebenfalls eine Textdatei namens poem.txt im gleichen Verzeichnis.

Beispiel exceptions_using_with.py

Quellcode
1with open("poem.txt") as f:
2    for line in f:
3        print(line, end='')

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Wie es funktioniert

Die Ausgabe sollte dieselbe sein wie im vorherigen Beispiel. Der Unterschied besteht hier darin, dass wir die Funktion open zusammen mit der Anweisung with verwenden – wir überlassen das Schließen der Datei der automatischen Behandlung durch with open.

Hinter den Kulissen geschieht Folgendes: Es existiert ein Protokoll, das von der Anweisung with verwendet wird. Dabei wird das Objekt abgerufen, das durch die Anweisung open zurückgegeben wird; nennen wir es in diesem Fall „thefile“.

Es wird immer die Funktion thefile.__enter__ aufgerufen, bevor der darunterliegende Codeblock ausgeführt wird, und immer thefile.__exit__, nachdem der Codeblock beendet wurde.

Daher sollte der Code, den wir andernfalls in einem finally-Block geschrieben hätten, automatisch von der Methode __exit__ übernommen werden. Dies hilft uns dabei, die wiederholte Verwendung expliziter try...finally-Anweisungen zu vermeiden.

Eine weiterführende Diskussion dieses Themas liegt außerhalb des Umfangs dieses Buches. Bitte lesen Sie daher PEP 343 für eine umfassende Erklärung.

Zusammenfassung#

Wir haben die Verwendung der Anweisungen try..except und try..finally besprochen. Außerdem haben wir gesehen, wie eigene Ausnahmetypen erstellt und wie Ausnahmen ausgelöst werden können.

Als Nächstes werden wir die Python-Standardbibliothek untersuchen.