Umgang mit Ausnahmen in Python: Eine detaillierte visuelle Einführung

Herzlich willkommen! In diesem Artikel erfahren Sie, wie Sie mit Ausnahmen in Python umgehen.

Insbesondere werden wir Folgendes behandeln:

  • Ausnahmen
  • Der Zweck der Ausnahmebehandlung
  • Die try-Klausel
  • Die Ausnahmeklausel
  • Die else-Klausel
  • Die finally-Klausel
  • Wie man Ausnahmen auslöst

Sind Sie bereit? Lass uns anfangen! 😀

1️⃣ Einführung in Ausnahmen

Wir werden mit Ausnahmen beginnen:

  • Was sind sie?
  • Warum sind sie relevant?
  • Warum sollten Sie damit umgehen?

Laut der Python-Dokumentation:

Während der Ausführung erkannte Fehler werden als Ausnahmen bezeichnet und sind nicht unbedingt schwerwiegend.

Ausnahmen werden ausgelöst, wenn das Programm während seiner Ausführung auf einen Fehler stößt. Sie stören den normalen Programmfluss und beenden ihn normalerweise abrupt. Um dies zu vermeiden, können Sie sie fangen und entsprechend behandeln.

Sie haben sie wahrscheinlich während Ihrer Programmierprojekte gesehen.

Wenn Sie jemals versucht haben, in Python durch Null zu teilen, muss diese Fehlermeldung angezeigt worden sein:

>>> a = 5/0 Traceback (most recent call last): File "", line 1, in  a = 5/0 ZeroDivisionError: division by zero

Wenn Sie versucht haben, eine Zeichenfolge mit einem ungültigen Index zu indizieren, wird definitiv die folgende Fehlermeldung angezeigt:

>>> a = "Hello, World" >>> a[456] Traceback (most recent call last): File "", line 1, in  a[456] IndexError: string index out of range

Dies sind Beispiele für Ausnahmen.

🔹 Häufige Ausnahmen

Es gibt viele verschiedene Arten von Ausnahmen, die alle in bestimmten Situationen auftreten. Einige der Ausnahmen, die Sie höchstwahrscheinlich bei der Arbeit an Ihren Projekten sehen werden, sind:

  • IndexError - Wird ausgelöst , wenn Sie versuchen, eine Liste, ein Tupel oder eine Zeichenfolge über die zulässigen Grenzen hinaus zu indizieren. Zum Beispiel:
>>> num = [1, 2, 6, 5] >>> num[56546546] Traceback (most recent call last): File "", line 1, in  num[56546546] IndexError: list index out of range
  • KeyError - Wird ausgelöst , wenn Sie versuchen, auf den Wert eines Schlüssels zuzugreifen, der in einem Wörterbuch nicht vorhanden ist. Zum Beispiel:
>>> students = {"Nora": 15, "Gino": 30} >>> students["Lisa"] Traceback (most recent call last): File "", line 1, in  students["Lisa"] KeyError: 'Lisa'
  • NameError - wird ausgelöst , wenn ein Name, auf den Sie im Code verweisen, nicht vorhanden ist. Zum Beispiel:
>>> a = b Traceback (most recent call last): File "", line 1, in  a = b NameError: name 'b' is not defined
  • TypeError - Wird ausgelöst , wenn eine Operation oder Funktion auf ein Objekt eines unangemessenen Typs angewendet wird. Zum Beispiel:
>>> (5, 6, 7) * (1, 2, 3) Traceback (most recent call last): File "", line 1, in  (5, 6, 7) * (1, 2, 3) TypeError: can't multiply sequence by non-int of type 'tuple'
  • ZeroDivisionError - Wird ausgelöst , wenn Sie versuchen, durch Null zu teilen.
>>> a = 5/0 Traceback (most recent call last): File "", line 1, in  a = 5/0 ZeroDivisionError: division by zero

💡 Tipps: Weitere Informationen zu anderen Arten von integrierten Ausnahmen finden Sie in diesem Artikel in der Python-Dokumentation.

🔸 Anatomie einer Ausnahme

Ich bin sicher, dass Sie in diesen Fehlermeldungen ein allgemeines Muster bemerkt haben müssen. Lassen Sie uns ihre allgemeine Struktur Stück für Stück aufteilen:

Zuerst finden wir diese Zeile (siehe unten). Ein Traceback ist im Grunde eine Liste mit den Funktionsaufrufen, die vor dem Auslösen der Ausnahme ausgeführt wurden.

Der Traceback hilft Ihnen beim Debuggen, da Sie die Reihenfolge der Funktionsaufrufe analysieren können, die zur Ausnahme geführt haben:

Traceback (most recent call last):

Dann sehen wir diese Zeile (siehe unten) mit dem Pfad zur Datei und der Zeile, die die Ausnahme ausgelöst hat. In diesem Fall war der Pfad die Python-Shell, da das Beispiel direkt in IDLE ausgeführt wurde.

File "", line 1, in  a - 5/0

💡 Tipp: Wenn die Zeile, die die Ausnahme ausgelöst hat, zu einer Funktion gehört, wird sie durch den Namen der Funktion ersetzt.

Schließlich wird eine beschreibende Meldung angezeigt, in der die Art der Ausnahme aufgeführt ist und zusätzliche Informationen zum Debuggen des Codes bereitgestellt werden:

NameError: name 'a' is not defined

2️⃣ Ausnahmebehandlung: Zweck und Kontext

Sie fragen sich vielleicht: Warum sollte ich Ausnahmen behandeln wollen? Warum ist das für mich hilfreich? Durch die Behandlung von Ausnahmen können Sie einen alternativen Ausführungsfluss bereitstellen, um einen unerwarteten Absturz Ihres Programms zu vermeiden.

🔹 Beispiel: Benutzereingabe

Stellen Sie sich vor, was passieren würde, wenn ein Benutzer, der mit Ihrem Programm arbeitet, eine ungültige Eingabe eingibt. Dies würde eine Ausnahme auslösen, da während des Prozesses eine ungültige Operation ausgeführt wurde.

Wenn Ihr Programm dies nicht richtig handhabt, stürzt es plötzlich ab und der Benutzer hat eine sehr enttäuschende Erfahrung mit Ihrem Produkt.

Wenn Sie die Ausnahme jedoch behandeln, können Sie eine Alternative bereitstellen, um die Benutzererfahrung zu verbessern.

Perhaps you could display a descriptive message asking the user to enter a valid input, or you could provide a default value for the input. Depending on the context, you can choose what to do when this happens, and this is the magic of error handling. It can save the day when unexpected things happen. ⭐️

🔸 What Happens Behind the Scenes?

Basically, when we handle an exception, we are telling the program what to do if the exception is raised. In that case, the "alternative" flow of execution will come to the rescue. If no exceptions are raised, the code will run as expected.

3️⃣ Time to Code: The try ... except Statement

Now that you know what exceptions are and why you should we handle them, we will start diving into the built-in tools that the Python languages offers for this purpose.

First, we have the most basic statement: try ... except.

Let's illustrate this with a simple example. We have this small program that asks the user to enter the name of a student to display his/her age:

students = {"Nora": 15, "Gino": 30} def print_student_age(): name = input("Please enter the name of the student: ") print(students[name]) print_student_age()

Notice how we are not validating user input at the moment, so the user might enter invalid values (names that are not in the dictionary) and the consequences would be catastrophic because the program would crash if a KeyError is raised:

# User Input Please enter the name of the student: "Daniel" # Error Message Traceback (most recent call last): File "", line 15, in  print_student_age() File "", line 13, in print_student_age print(students[name]) KeyError: '"Daniel"'

🔹 Syntax

We can handle this nicely using try ... except. This is the basic syntax:

In our example, we would add the try ... except statement within the function. Let's break this down piece by piece:

students = {"Nora": 15, "Gino": 30} def print_student_age(): while True: try: name = input("Please enter the name of the student: ") print(students[name]) break except: print("This name is not registered") print_student_age()

If we "zoom in", we see the try ... except statement:

try: name = input("Please enter the name of the student: ") print(students[name]) break except: print("This name is not registered")
  • When the function is called, the try clause will run. If no exceptions are raised, the program will run as expected.
  • But if an exception is raised in the try clause, the flow of execution will immediately jump to the except clause to handle the exception.

💡 Note: This code is contained within a while loop to continue asking for user input if the value is invalid. This is an example:

Please enter the name of the student: "Lulu" This name is not registered Please enter the name of the student: 

This is great, right? Now we can continue asking for user input if the value is invalid.

At the moment, we are handling all possible exceptions with the same except clause. But what if we only want to handle a specific type of exception? Let's see how we could do this.

🔸 Catching Specific Exceptions

Since not all types of exceptions are handled in the same way, we can specify which exceptions we would like to handle with this syntax:

This is an example. We are handling the ZeroDivisionError exception in case the user enters zero as the denominator:

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except ZeroDivisionError: print("Please enter a valid denominator.") divide_integers()

Dies wäre das Ergebnis:

# First iteration Please enter the numerator: 5 Please enter the denominator: 0 Please enter a valid denominator. # Second iteration Please enter the numerator: 5 Please enter the denominator: 2 2.5

Wir gehen damit richtig um. Aber ... wenn eine andere Art von Ausnahme ausgelöst wird, wird das Programm diese nicht ordnungsgemäß behandeln.

Hier haben wir ein Beispiel für einen ValueError, da einer der Werte ein float und kein int ist:

Please enter the numerator: 5 Please enter the denominator: 0.5 Traceback (most recent call last): File "", line 53, in  divide_integers() File "", line 47, in divide_integers b = int(input("Please enter the denominator: ")) ValueError: invalid literal for int() with base 10: '0.5'

Wir können anpassen, wie wir mit verschiedenen Arten von Ausnahmen umgehen.

🔹 Mehrere Ausnahmeklauseln

Dazu müssen wir mehrere exceptKlauseln hinzufügen , um verschiedene Arten von Ausnahmen unterschiedlich zu behandeln.

Laut der Python-Dokumentation:

Eine try-Anweisung kann mehr als eine Ausnahmeklausel enthalten , um Handler für verschiedene Ausnahmen anzugeben. Es wird höchstens ein Handler ausgeführt .

In diesem Beispiel haben wir zwei Ausnahmen. Einer von ihnen behandelt ZeroDivisionError und der andere ValueError, die beiden Arten von Ausnahmen, die in diesem try-Block ausgelöst werden könnten.

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except ZeroDivisionError: print("Please enter a valid denominator.") except ValueError: print("Both values have to be integers.") divide_integers() 

💡 Tipp: Sie müssen festlegen, welche Arten von Ausnahmen im try-Block ausgelöst werden können , um sie angemessen zu behandeln.

🔸 Mehrere Ausnahmen, eine Ausnahmeklausel

Sie können auch verschiedene Arten von Ausnahmen mit derselben Ausnahmeklausel behandeln.

Laut der Python-Dokumentation:

Eine Ausnahmeklausel kann mehrere Ausnahmen als Tupel in Klammern benennen .

Dies ist ein Beispiel, in dem zwei Ausnahmen (ZeroDivisionError und ValueError) mit derselben exceptKlausel abgefangen werden :

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) except (ZeroDivisionError, ValueError): print("Please enter valid integers.") divide_integers()

Die Ausgabe wäre für die beiden Arten von Ausnahmen gleich, da sie von derselben Ausnahmeklausel behandelt werden:

Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers.
Please enter the numerator: 0.5 Please enter valid integers. Please enter the numerator: 

🔹 Behandlung von Ausnahmen, die durch in der try-Klausel aufgerufene Funktionen ausgelöst wurden

Ein interessanter Aspekt der Ausnahmebehandlung ist, dass wenn eine Ausnahme in einer Funktion ausgelöst wird, die zuvor in der try-Klausel einer anderen Funktion aufgerufen wurde, und die Funktion selbst sie nicht behandelt, der Aufrufer sie behandelt, wenn eine geeignete Ausnahmeklausel vorhanden ist.

Laut der Python-Dokumentation:

Ausnahmebehandlungsroutinen behandeln Ausnahmen nicht nur, wenn sie sofort in der try-Klausel auftreten, sondern auch, wenn sie in Funktionen auftreten, die in der try-Klausel (auch indirekt) aufgerufen werden.

Sehen wir uns ein Beispiel an, um dies zu veranschaulichen:

def f(i): try: g(i) except IndexError: print("Please enter a valid index") def g(i): a = "Hello" return a[i] f(50)

Wir haben die fFunktion und die gFunktion. fruft gin der try-Klausel auf. gLöst mit dem Argument 50 einen IndexError aus, da der Index 50 für die Zeichenfolge a nicht gültig ist.

But g itself doesn't handle the exception. Notice how there is no try ... except statement in the g function. Since it doesn't handle the exception, it "sends" it to f to see if it can handle it, as you can see in the diagram below:

Since f does know how to handle an IndexError, the situation is handled gracefully and this is the output:

Please enter a valid index

💡 Note: If f had not handled the exception, the program would have ended abruptly with the default error message for an IndexError.

🔸 Accessing Specific Details of Exceptions

Exceptions are objects in Python, so you can assign the exception that was raised to a variable. This way, you can print the default description of the exception and access its arguments.

Laut der Python-Dokumentation:

Die Ausnahmeklausel kann eine Variable nach dem Ausnahmenamen angeben . Die Variable ist an eine Ausnahmeinstanz gebunden, deren Argumente in instance.args gespeichert sind.

Hier haben wir ein Beispiel (siehe unten), in dem wir ZeroDivisionErrorder Variablen die Instanz von zuweisen e. Anschließend können wir diese Variable in der Ausnahmeklausel verwenden, um auf den Typ der Ausnahme, ihre Nachricht und Argumente zuzugreifen.

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) print(a / b) # Here we assign the exception to the variable e except ZeroDivisionError as e: print(type(e)) print(e) print(e.args) divide_integers()

Die entsprechende Ausgabe wäre:

Please enter the numerator: 5 Please enter the denominator: 0 # Type  # Message division by zero # Args ('division by zero',)

💡 Tipp: Wenn Sie mit speziellen Methoden vertraut sind, laut Python-Dokumentation: "Der Einfachheit halber definiert die Ausnahmeinstanz, __str__()dass die Argumente direkt gedruckt werden können, ohne dass auf sie verwiesen werden muss .args."

4️⃣ Jetzt fügen wir hinzu: Die "else" -Klausel

Die elseKlausel ist optional, aber ein großartiges Tool, mit dem wir Code ausführen können, der nur ausgeführt werden sollte, wenn in der try-Klausel keine Ausnahmen ausgelöst wurden.

Laut der Python-Dokumentation:

Die Anweisung tryexceptenthält eine optionale else-Klausel , die, sofern vorhanden, allen außer Klauseln folgen muss. Dies ist nützlich für Code, der ausgeführt werden muss, wenn die try-Klausel keine Ausnahme auslöst.

Hier ist ein Beispiel für die Verwendung der elseKlausel:

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) result = a / b except (ZeroDivisionError, ValueError): print("Please enter valid integers. The denominator can't be zero") else: print(result) divide_integers()

Wenn keine Ausnahme ausgelöst wird, wird das Ergebnis gedruckt:

Please enter the numerator: 5 Please enter the denominator: 5 1.0

Wenn jedoch eine Ausnahme ausgelöst wird, wird das Ergebnis nicht gedruckt:

Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers. The denominator can't be zero

💡 Tipp: Laut Python-Dokumentation:

Die Verwendung der elseKlausel ist besser als das Hinzufügen von zusätzlichem Code zur tryKlausel, da dadurch vermieden wird, dass versehentlich eine Ausnahme abgefangen wird, die nicht durch den durch die Anweisung try… geschützten Code exceptausgelöst wurde.

5️⃣ Die "Endlich" -Klausel

Die finally-Klausel ist die letzte Klausel in dieser Reihenfolge. Es ist optional , aber wenn Sie es einschließen, muss es die letzte Klausel in der Sequenz sein. Die finallyKlausel wird immer ausgeführt, auch wenn in der try-Klausel eine Ausnahme ausgelöst wurde.  

Laut der Python-Dokumentation:

Wenn eine finallyKlausel vorhanden ist, wird die finallyKlausel als letzte Aufgabe ausgeführt, bevor die tryAnweisung abgeschlossen ist. Die finallyKlausel wird ausgeführt, unabhängig davon, ob die tryAnweisung eine Ausnahme erzeugt.

Die finally-Klausel wird normalerweise verwendet, um "Bereinigungs" -Aktionen auszuführen, die immer abgeschlossen sein sollten. Wenn wir beispielsweise mit einer Datei in der try-Klausel arbeiten, müssen wir die Datei immer schließen, auch wenn bei der Arbeit mit den Daten eine Ausnahme ausgelöst wurde.

Hier ist ein Beispiel für die finally-Klausel:

def divide_integers(): while True: try: a = int(input("Please enter the numerator: ")) b = int(input("Please enter the denominator: ")) result = a / b except (ZeroDivisionError, ValueError): print("Please enter valid integers. The denominator can't be zero") else: print(result) finally: print("Inside the finally clause") divide_integers()

Dies ist die Ausgabe, wenn keine Ausnahmen ausgelöst wurden:

Please enter the numerator: 5 Please enter the denominator: 5 1.0 Inside the finally clause

Dies ist die Ausgabe, als eine Ausnahme ausgelöst wurde:

Please enter the numerator: 5 Please enter the denominator: 0 Please enter valid integers. The denominator can't be zero Inside the finally clause

Beachten Sie, wie die finallyKlausel immer ausgeführt wird .

❗️Important: remember that the else clause and the finally clause are optional, but if you decide to include both, the finally clause has to be the last clause in the sequence.

6️⃣ Raising Exceptions

Now that you know how to handle exceptions in Python, I would like to share with you this helpful tip: you can also choose when to raise exceptions in your code.

This can be helpful for certain scenarios. Let's see how you can do this:

This line will raise a ValueError with a custom message.

Here we have an example (see below) of a function that prints the value of the items of a list or tuple, or the characters in a string. But you decided that you want the list, tuple, or string to be of length 5. You start the function with an if statement that checks if the length of the argument data is 5. If it isn't, a ValueError exception is raised:

def print_five_items(data): if len(data) != 5: raise ValueError("The argument must have five elements") for item in data: print(item) print_five_items([5, 2])

The output would be:

Traceback (most recent call last): File "", line 122, in  print_five_items([5, 2]) File "", line 117, in print_five_items raise ValueError("The argument must have five elements") ValueError: The argument must have five elements

Notice how the last line displays the descriptive message:

ValueError: The argument must have five elements

You can then choose how to handle the exception with a try ... except statement. You could add an else clause and/or a finally clause. You can customize it to fit your needs.

🔹 Helpful Resources

  • Exceptions
  • Handling Exceptions
  • Defining Clean-up Actions

I hope you enjoyed reading my article and found it helpful. Now you have the necessary tools to handle exceptions in Python and you can use them to your advantage when you write Python code. ? Check out my online courses. You can follow me on Twitter.

⭐️ You may enjoy my other freeCodeCamp /news articles:

  • The @property Decorator in Python: Its Use Cases, Advantages, and Syntax
  • Data Structures 101: Graphs — A Visual Introduction for Beginners
  • Data Structures 101: Arrays — A Visual Introduction for Beginners