Python3-Fehler und Ausnahmen
Als Python-Anfänger sehen Sie beim ersten Erlernen der Python-Programmierung oft einige Fehlermeldungen. Wir haben sie bisher nicht erwähnt; dieses Kapitel wird sie speziell vorstellen.
Python hat zwei Arten von Fehlern, die leicht zu erkennen sind: Syntaxfehler und Ausnahmen.
Python assert (Assertion) wird verwendet, um einen Ausdruck auszuwerten, und löst eine Ausnahme aus, wenn die Ausdrucksbedingung falsch ist.

Syntaxfehler
Python-Syntaxfehler, auch Parsing-Fehler genannt, treten bei Anfängern häufig auf, wie im folgenden Beispiel:
File "<stdin>", line 1, in ?
while True print('Hello world')
^
SyntaxError: ungültige Syntax
In diesem Beispiel wird festgestellt, dass die Funktion print() einen Fehler enthält: Es fehlt ein Doppelpunkt davor.: 。
Der Syntax-Parser zeigt auf die Zeile mit dem Fehler und markiert einen kleinen Pfeil an der Position, an der der Fehler zuerst gefunden wurde.
Ausnahmen
Selbst wenn die Syntax eines Python-Programms korrekt ist, können beim Ausführen trotzdem Fehler auftreten. Fehler, die zur Laufzeit erkannt werden, werden Ausnahmen genannt.
Die meisten Ausnahmen werden vom Programm nicht behandelt und erscheinen hier in Form von Fehlermeldungen:
Beispiel
Traceback (jüngster Aufruf zuletzt):
File "<stdin>", line 1, in ?
ZeroDivisionError: Division durch Null
>>> 4 + spam*3 # spam ist nicht definiert, löst eine Ausnahme aus
Traceback (jüngster Aufruf zuletzt):
File "<stdin>", line 1, in ?
NameError: Name 'spam' ist nicht definiert
>>> '2' + 2 # int kann nicht zu str addiert werden, löst eine Ausnahme aus
Traceback (jüngster Aufruf zuletzt):
Datei "<stdin>", Zeile 1, in <Modul>
TypeError: kann nur str (nicht "int") verketten str
Ausnahmen treten in verschiedenen Typen auf, und diese Typen werden alle als Teil der Information ausgegeben: Die Typen im Beispiel sind ZeroDivisionError, NameError und TypeError.
Der vordere Teil der Fehlermeldung zeigt den Kontext, in dem die Ausnahme aufgetreten ist, und gibt spezifische Informationen in Form eines Aufrufstapels aus.
Ausnahmebehandlung
try/except
Ausnahmen können mit dertry/except-Anweisung abgefangen werden.

Im folgenden Beispiel wird der Benutzer aufgefordert, eine gültige Ganzzahl einzugeben, aber dem Benutzer ist es erlaubt, das Programm zu unterbrechen (mit Control-C oder einer vom Betriebssystem bereitgestellten Methode). Die Unterbrechung durch den Benutzer löst eine KeyboardInterrupt-Ausnahme aus.
try:
x = int(input("Bitte geben Sie eine Zahl ein: "))
break
except ValueError:
print("Sie haben keine Zahl eingegeben, bitte versuchen Sie es erneut!")
Die try-Anweisung funktioniert wie folgt:
Zuerst wird die try-Klausel ausgeführt (die Anweisungen zwischen dem Schlüsselwort try und dem Schlüsselwort except).
Wenn keine Ausnahme auftritt, wird die except-Klausel ignoriert, und die try-Klausel endet nach der Ausführung.
Wenn während der Ausführung der try-Klausel eine Ausnahme auftritt, wird der Rest der try-Klausel ignoriert. Wenn der Typ der Ausnahme mit dem Namen nach except übereinstimmt, wird die entsprechende except-Klausel ausgeführt.
Wenn eine Ausnahme mit keinem except übereinstimmt, wird diese Ausnahme an ein äußeres try weitergegeben.
Eine try-Anweisung kann mehrere except-Klauseln enthalten, um verschiedene spezifische Ausnahmen jeweils zu behandeln. Höchstens ein Zweig wird ausgeführt.
Der Handler behandelt nur Ausnahmen in der entsprechenden try-Klausel, nicht Ausnahmen in Handlern anderer try-Anweisungen.
Eine except-Klausel kann mehrere Ausnahmen gleichzeitig behandeln; diese Ausnahmen werden in Klammern gesetzt, um ein Tupel zu bilden, zum Beispiel:
pass
Die letzte except-Klausel kann den Ausnahmenamen weglassen, und sie wird als Platzhalter verwendet. Sie können diese Methode verwenden, um eine Fehlermeldung auszugeben und die Ausnahme dann erneut auszulösen.
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
try/except...else
try/exceptDie Anweisung hat außerdem eine optionaleelse-Klausel. Wenn diese Klausel verwendet wird, muss sie nach allen except-Klauseln stehen.
Die else-Klausel wird ausgeführt, wenn in der try-Klausel keine Ausnahme auftritt.

Das folgende Beispiel prüft in einer try-Anweisung, ob die Datei geöffnet werden kann. Wenn die Datei ohne Ausnahme normal geöffnet wird, wird der else-Teil ausgeführt, um den Dateiinhalt zu lesen:
try:
f = open(arg, 'r')
except IOError:
print('kann nicht öffnen', arg)
else:
print(arg, 'hat', len(f.readlines()), 'Zeilen')
f.close()
Die Verwendung der else-Klausel ist besser, als alle Anweisungen in die try-Klausel zu setzen, weil sie einige unerwartete Ausnahmen vermeidet, die except nicht abfangen kann.
Die Ausnahmebehandlung behandelt nicht nur Ausnahmen, die direkt in der try-Klausel auftreten, sondern auch Ausnahmen, die in Funktionen ausgelöst werden, die in der Klausel aufgerufen werden (sogar indirekt aufgerufene Funktionen). Zum Beispiel:
x = 1/0
>>> try:
this_fails()
except ZeroDivisionError as err:
print('Laufzeitfehler wird behandelt:', err)
Laufzeitfehler wird behandelt: int division or Modulo durch Null
try-finally-Anweisung
Die try-finally-Anweisung führt den abschließenden Code aus, unabhängig davon, ob eine Ausnahme auftritt.

Im folgenden Beispiel wird die finally-Anweisung ausgeführt, unabhängig davon, ob eine Ausnahme auftritt:
Beispiel
example()
except AssertionError as error:
print(error)
else:
try:
with open('file.log') as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
finally:
print('Dieser Satz wird unabhängig davon ausgeführt, ob eine Ausnahme auftritt.')
Ausnahmen auslösen
Python verwendet die raise-Anweisung, um eine bestimmte Ausnahme auszulösen.
Das Format der raise-Syntax lautet wie folgt:
raise [Exception [, args [, traceback]]]

Im folgenden Beispiel wird eine Ausnahme ausgelöst, wenn x größer als 5 ist:
if x > 5:
raise Exception('x darf nicht größer als 5 sein. Der Wert von x ist: {}'.format(x))
Das Ausführen des obigen Codes löst eine Ausnahme aus:
Traceback (most recent call last):
File "test.py", line 3, in <module>
raise Exception('x 不能大于 5。x 的值为: {}'.format(x))
Exception: x 不能大于 5。x 的值为: 10
Der einzige Parameter von raise gibt die auszulösende Ausnahme an. Er muss eine Instanz einer Ausnahme oder einer Ausnahmeklasse sein (d. h. eine Unterklasse von Exception).
Wenn Sie nur wissen möchten, ob eine Ausnahme ausgelöst wurde, und sie nicht behandeln möchten, kann eine einfache raise-Anweisung sie erneut auslösen.
raise NameError('HiThere') # Simuliere eine Ausnahme.
except NameError:
print('Eine Ausnahme flog vorbei!')
raise
Eine Ausnahme flog vorbei!
Traceback (jüngster Aufruf zuletzt):
File "<stdin>", line 2, in ?
NameError: HiThere
Benutzerdefinierte Ausnahmen
Sie können Ihre eigenen Ausnahmen haben, indem Sie eine neue Ausnahmeklasse erstellen. Die Ausnahmeklasse erbt entweder direkt oder indirekt von der Klasse Exception, zum Beispiel:
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
>>> try:
raise MyError(2*2)
except MyError as e:
print('Meine Exception ist aufgetreten, Wert:', e.value)
Meine Exception ist aufgetreten, Wert: 4
>>> raise MyError('Ups!')
Traceback (jüngster Aufruf zuletzt):
File "<stdin>", line 1, in ?
__main__.MyError: 'Ups!'
In diesem Beispiel wird die Standard-__init__() der Exception-Klasse überschrieben.
Wenn ein Modul mehrere verschiedene Exceptions auslösen kann, ist es üblich, eine Basis-Exception-Klasse für dieses Paket zu erstellen und dann auf Basis dieser Basisklasse verschiedene Unterklassen für unterschiedliche Fehlersituationen zu erstellen:
"""Basisklasse für Exceptions in diesem Modul."""
pass
class InputError(Error):
"""Exception ausgelöst bei Fehlern in der Eingabe.
Attribute:
expression -- Eingabeausdruck, in dem der Fehler aufgetreten ist
message -- Erklärung des Fehlers
"""
def __init__(self, expression, message):
self.expression = expression
self.message = message
class TransitionError(Error):
"""Wird ausgelöst, wenn eine Operation einen Zustandsübergang versucht, der nicht
erlaubt ist.
Attribute:
previous -- Zustand zu Beginn des Übergangs
next -- angestrebter neuer Zustand
message -- Erklärung, warum der spezifische Übergang nicht erlaubt ist
"""
def __init__(self, previous, next, message):
self.vorherige = vorherige
self.nächste = nächste
self.message = message
Die meisten Ausnahmenamen enden mit 'Error', genau wie die standardmäßige Benennung von Ausnahmen.
Definieren von Aufräumaktionen
Die try-Anweisung hat eine weitere optionale Klausel, die Aufräumaktionen definiert, die unter allen Umständen ausgeführt werden. Zum Beispiel:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
...
Goodbye, world!
Traceback (jüngster Aufruf zuletzt):
Datei "<stdin>", Zeile 2, in <Modul>
KeyboardInterrupt
Im obigen Beispiel wird die finally-Klausel unabhängig davon ausgeführt, ob eine Ausnahme in der try-Klausel auftritt.
Wenn eine Ausnahme in der try-Klausel (oder in den except- und else-Klauseln) ausgelöst wird und keine except-Klausel sie abfängt, dann wird diese Ausnahme ausgelöst, nachdem die finally-Klausel ausgeführt wurde.
Das folgende ist ein komplexeres Beispiel (das sowohl except- als auch finally-Klauseln in derselben try-Anweisung enthält):
try:
result = x / y
except ZeroDivisionError:
print("division by zero!")
else:
print("Ergebnis ist", result)
finally:
print("finally-Klausel wird ausgeführt")
>>> divide(2, 1)
result ist 2.0
Ausführen der finally- Klausel
>>> divide(2, 0)
Division durch Null!
Ausführen der finally- Klausel
>>> divide("2", "1")
Ausführen der finally- Klausel
Traceback (jüngster Aufruf zuletzt):
File "<stdin>", line 1, in ?
Datei "<stdin>", Zeile 3, in divide
TypeError: nicht unterstützter Operand typ(s) für /: 'str' und 'str'
Vordefinierte Bereinigungsaktionen
Einige Objekte definieren ein standardmäßiges Bereinigungsverhalten. Unabhängig davon, ob das System es erfolgreich genutzt hat, wird dieses standardmäßige Bereinigungsverhalten ausgeführt, sobald es nicht mehr benötigt wird.
Das folgende Beispiel zeigt den Versuch, eine Datei zu öffnen und anschließend den Inhalt auf dem Bildschirm auszugeben:
print(line, end="")
Das Problem mit dem obigen Code ist, dass nach der Ausführung die Datei geöffnet bleibt und nicht geschlossen wird.
Die with-Schlüsselwort-Anweisung kann sicherstellen, dass Objekte wie Dateien nach der Verwendung ihre Bereinigungsmethode korrekt ausführen:
for line in f:
print(line, end="")
Nachdem der obige Code ausgeführt wurde, wird die Datei f immer geschlossen, selbst wenn während der Verarbeitung ein Problem auftritt.
Weitere Inhalte zum with-Schlüsselwort finden Sie unter:Python with-Schlüsselwort