31. Ausnahmen¶
Möchtest du verhindern, dass dem Benutzer eine rote Python-Fehlermeldung angezeigt wird, wenn ein Problem mit deinem Programm auftritt? Möchtest du verhindern, dass dein Programm hängt? Wenn ja, dann brauchst du Ausnahmen (englisch exceptions).
Ausnahmen werden verwendet, um abnormale Bedingungen zu behandeln, die während der Ausführung von Programmcode auftreten können. Ausnahmen werden häufig bei Datei- und Netzwerkoperationen verwendet. Auf diese Weise kann das Programm ordnungsgemäß mit Speicherplatzmangel, Netzwerkfehlern oder Berechtigungsfehlern umgehen.
31.1. Wortschatz¶
Beim Arbeiten mit Ausnahmen werden mehrere Begriffe und Phrasen verwendet. Hier sind die häufigsten:
Ausnahme: Dieser Begriff kann eines von zwei Dingen bedeuten. Erstens die Bedingung, die zu einem abnormalen Programmfluss führt. Oder er könnte verwendet werden, um auf ein Objekt zu verweisen, das die Daten der Ausnahmebedingung darstellt. Jede Ausnahme hat ein Objekt, das Informationen darüber enthält.
Ausnahmebehandlung: Der Prozess der Behandlung einer Ausnahme vom normalen Programmablauf.
Catch-Block oder Exception-Block: Code, der einen abnormalen Zustand behandelt, soll die Ausnahme „abfangen“.
Auslösen oder Werfen: Wenn eine abnormale Bedingung für den Programmablauf erkannt wurde, wird ein Exemplar eines Ausnahmeobjekts erstellt. Es wird dann „geworfen“ oder „ausgelöst“ zum Code, der es abfängt.
Unbehandelte Ausnahme oder nicht erfasste Ausnahme: Eine Ausnahme, die ausgelöst, aber nie abgefangen wird. Dies führt normalerweise zu einem Fehler und zum Beenden oder Abstürzen des Programms.
Try Block: Ein Codeblock, in dem möglicherweise eine Ausnahme ausgelöst wird.
Die meisten Programmiersprachen verwenden die Begriffe „throw“ und „catch“. Python tut dies leider nicht. Python verwendet „raise“ und „exception“. Wir stellen hier das Wurf-/Fangvokabular vor, da es die in der Branche am häufigsten verwendeten Begriffe sind.
31.2. Ausnahmebehandlung¶
Der Code für die Behandlung von Ausnahmen ist einfach. Siehe folgendes Beispiel:
1 2 3 4 5 | # Divide by zero
try:
x = 5 / 0
except:
print("Error dividing by zero")
|
In Zeile zwei steht die Anweisung try
. Jede eingerückte Zeile darunter ist Teil des „try-Blocks“. Unter dem try
-Block muss eingerückter Code stehen. Erst der Teil, der mit einer except
-Anweisung beginnt, ist nicht mehr eingerückt. Die try
-Anweisung definiert einen Codeabschnitt, den der Code auszuführen versucht.
Wenn während der Verarbeitung des Codes eine Ausnahme auftritt, springt die Ausführung sofort zum „catch-Block“. Dieser Codeblock wird unter der Anweisung except
in Zeile 4 eingerückt. Dieser Code ist für die Behandlung des Fehlers verantwortlich.
Ein Programm kann Ausnahmen verwenden, um Fehler abzufangen, die bei der Konvertierung von Text in eine Zahl auftreten. Zum Beispiel:
1 2 3 4 5 | # Invalid number conversion
try:
x = int("fred")
except:
print("Error converting fred to a number")
|
In Zeile 3 wird eine Ausnahme ausgelöst, da „fred“ nicht in eine Ganzzahl konvertiert werden kann. Der Code in Zeile 5 gibt eine Fehlermeldung aus.
Unten findest du eine erweiterte Version dieses Beispiels. Es überprüft die Eingaben eines Benutzers auf Fehler, um sicherzustellen, dass eine Ganzzahl eingegeben wurde. Wenn der Benutzer keine Ganzzahl eingibt, fragt das Programm weiterhin nach einer Ganzzahl. Der Code verwendet die Ausnahmebehandlung, um einen möglichen Konvertierungsfehler zu erfassen, der in Zeile 5 auftreten kann. Wenn der Benutzer etwas anderes als eine Ganzzahl eingibt, wird eine Ausnahme ausgelöst, wenn die Konvertierung in eine Zahl in Zeile 5 erfolgt. Der Code in Zeile 6, der number_entered
auf True
setzt, wird nicht ausgeführt, wenn in Zeile 5 eine Ausnahme vorliegt.
1 2 3 4 5 6 7 8 | number_entered = False
while not number_entered:
number_string = input("Enter an integer: ")
try:
n = int(number_string)
number_entered = True
except:
print("Error, invalid integer")
|
Dateioperationen sind besonders fehleranfällig: Eine Festplatte könnte voll sein, ein Benutzer könnte eine Datei löschen, während sie geschrieben wird, sie könnte verschoben werden oder ein USB-Laufwerk könnte während des Betriebs herausgezogen werden. Diese Arten von Fehlern können ebenfalls mithilfe der Ausnahmebehandlung leicht erfasst werden.
1 2 3 4 5 | # Error opening file
try:
my_file = open("myfile.txt")
except:
print("Error opening file")
|
Mehrere Fehlertypen können erkannt und unterschiedlich verarbeitet werden. Es kann sinnvoll sein, dem Benutzer eine genauere Fehlermeldung zukommen zu lassen, als lediglich „ein Fehler ist aufgetreten“.
In the code below, different types of errors can occur from lines 3-15. By
placing IOError
after except
on line 19, only errors regarding Input and
Output (IO) will be handled by that code. Likewise line 21 only handles
errors around converting values, and line 23 covers division by zero errors.
The last exception handling occurs on line 25. Since line 25 does not include
a particular type of error, it will handle any error not covered by the except
blocks above. The „catch-all“ except
must always be last.
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 | # Multiple errors
try:
# Open the file
filename = "myfile.txt"
my_file = open(filename)
# Read from the file and strip any trailing line feeds
my_line = my_file.readline()
my_line = my_line.strip()
# Convert to a number
my_int = int(my_line)
# Do a calculation
my_calculated_value = 101 / my_int
except FileNotFoundError:
print(f"Could not find the file '{filename}'.")
except IOError:
print(f"Input/Output error when accessing the file '{filename}'.")
except ValueError:
print("Could not convert data to an integer.")
except ZeroDivisionError:
print("Division by zero error.")
except:
print("Unexpected error.")
|
Eine Liste der in Python integrierten Ausnahmen finden Sie unter folgender Adresse:
31.3. Beispiel: Highscore speichern¶
Das folgende Programm zeigt, wie du zwischen den Spielen einen Highscore speichern kannst. Der Spielstand wird in einer Datei mit dem Namen high_score.txt
gespeichert.
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 | """
Show how to use exceptions to save a high score for a game.
Sample Python/Pygame Programs
Simpson College Computer Science
http://simpson.edu/computer-science/
"""
def get_high_score():
# Default high score
high_score = 0
# Try to read the high score from a file
try:
high_score_file = open("high_score.txt", "r")
high_score = int(high_score_file.read())
high_score_file.close()
print("The high score is", high_score)
except IOError:
# Error reading file, no high score
print("There is no high score yet.")
except ValueError:
# There's a file there, but we don't understand the number.
print("I'm confused. Starting with no high score.")
return high_score
def save_high_score(new_high_score):
try:
# Write the file to disk
high_score_file = open("high_score.txt", "w")
high_score_file.write(str(new_high_score))
high_score_file.close()
except IOError:
# Hm, can't write it.
print("Unable to save the high score.")
def main():
""" Main program is here. """
# Get the high score
high_score = get_high_score()
# Get the score from the current game
current_score = 0
try:
# Ask the user for his/her score
current_score = int(input("What is your score? "))
except ValueError:
# Error, can't turn what they typed into a number
print("I don't understand what you typed.")
# See if we have a new high score
if current_score > high_score:
# We do! Save to disk
print("Yea! New high score!")
save_high_score(current_score)
else:
print("Better luck next time.")
# Call the main function, start up the game
if __name__ == "__main__":
main()
|
31.4. Ausnahmeobjekte¶
Weitere Informationen zu einem Fehler können aus dem Ausnahmenobjekt abgerufen werden. Dieses Objekt bekommt man, wenn ein Fehler mit dem Schlüsselwort as
abgefangen wird. Zum Beispiel:
1 2 3 4 | try:
x = 5 / 0
except ZeroDivisionError as e:
print(e)
|
Die Variable e
zeigt auf die weitere Information zu der Ausnahme, die ausgegeben werden kann. Mit Ausnahmeobjekten kann noch mehr getan werden, was jedoch den Rahmen dieses Kapitels sprengt. Weitere Informationen zum Ausnahmeobjekt findest du in der Python-Dokumentation online.
31.5. Ausnahmen generieren¶
Ausnahmen können mit dem Befehl raise
erzeugt werden. Zum Beispiel:
1 2 3 4 5 6 7 | # Generating exceptions
def get_input():
user_input = input("Enter something: ")
if len(user_input) == 0:
raise IOError("User entered nothing")
get_input()
|
Nimm den obigen Code und füge eine Ausnahmebehandlung für den ausgelösten IOError
hinzu.
Es ist auch möglich, benutzerdefinierte Ausnahmen zu erstellen, dies würde jedoch den Rahmen dieses Buches sprengen. Neugierige Leser erfahren mehr unter:
http://docs.python.org/tutorial/errors.html#raising-exceptions
31.6. Korrekte Verwendung von Ausnahmen¶
Ausnahmen sollten nicht verwendet werden, wenn if
-Anweisungen die Bedingung genauso gut verarbeiten können. Normaler Code sollte beim Ausführen des Szenarios „Happy Path“ keine Ausnahmen auslösen. Gut konstruierter Try/Catch-Code ist einfach nachzuverfolgen, aber Code mit vielen Ausnahmen und Code-Sprüngen zu verschiedenen Handlern kann ein Albtraum für das Debuggen sein. (Mir wurde einmal die Aufgabe zugewiesen, den Code zum Lesen eines XML-Dokuments zu debuggen. Es wurden Dutzende von Ausnahmen für jede Zeile der gelesenen Datei generiert. Es war unglaublich langsam und fehleranfällig. Dieser Code hätte niemals eine einzige Ausnahme beim normalen Lesen einer Datei generieren dürfen.)