Pythons integrierte Ausnahmen: Eine exemplarische Vorgehensweise mit Beispielen
Python verfügt über einen vollständigen Satz integrierter Ausnahmen, die eine schnelle und effiziente Möglichkeit bieten, Fehler und Ausnahmesituationen zu behandeln, die in Ihrem Code auftreten können. Für Sie als Python-Entwickler ist es von entscheidender Bedeutung, die am häufigsten verwendeten integrierten Ausnahmen zu kennen. Dieses Wissen hilft Ihnen beim Debuggen von Code, da jede Ausnahme eine spezifische Bedeutung hat, die Aufschluss über Ihren Debugging-Prozess geben kann.
Außerdem können Sie die meisten integrierten Ausnahmen in Ihrem Python-Code verarbeiten und auslösen. Dies ist eine hervorragende Möglichkeit, mit Fehlern und Ausnahmesituationen umzugehen, ohne eigene benutzerdefinierte Ausnahmen erstellen zu müssen.
In diesem Tutorial werden Sie:
- Erfahren Sie, was Fehler und Ausnahmen in Python sind
- Verstehen Sie, wie Python die integrierten Ausnahmen in einer Klassenhierarchie organisiert
- Entdecken Sie die am häufigsten verwendeten integrierten Ausnahmen
- Erfahren Sie, wie Sie in Ihrem Code integrierte Ausnahmen behandeln und auslösen
Um dieses Tutorial reibungslos durcharbeiten zu können, sollten Sie mit einigen Kernkonzepten in Python vertraut sein. Zu diesen Konzepten gehören Python-Klassen, Klassenhierarchien, Ausnahmen, try
… exclusive
-Blöcke und die raise
-Anweisung.
Fehler und Ausnahmen in Python
Fehler und Ausnahmen sind wichtige Konzepte in der Programmierung, und Sie werden in Ihrer Programmierkarriere wahrscheinlich viel Zeit damit verbringen, sich mit ihnen zu befassen. Fehler sind konkrete Bedingungen wie Syntax- und logische Fehler, die dazu führen, dass Ihr Code nicht richtig funktioniert oder sogar abstürzt.
Häufig können Sie Fehler beheben, indem Sie den Code aktualisieren oder ändern, eine neue Version einer Abhängigkeit installieren, die Logik des Codes überprüfen usw.
Angenommen, Sie müssen sicherstellen, dass eine bestimmte Zeichenfolge eine bestimmte Anzahl von Zeichen enthält. In diesem Fall können Sie die integrierte Funktion len()
verwenden:
>>> len("Pythonista") = 10
File "<input>", line 1
...
SyntaxError: cannot assign to function call here.
Maybe you meant '==' instead of '='?
In diesem Beispiel verwenden Sie den falschen Operator. Anstelle des Gleichheitsvergleichsoperators verwenden Sie den Zuweisungsoperator. Dieser Code löst einen SyntaxError
aus, der einen Syntaxfehler darstellt, wie der Name schon sagt.
Hinweis: Im obigen Code werden Sie feststellen, wie gut die Fehlermeldung eine mögliche Lösung zur Korrektur des Codes vorschlägt. Ab Version 3.10 haben die Python-Kernentwickler große Anstrengungen unternommen, um die Fehlermeldungen zu verbessern, um sie benutzerfreundlicher und nützlicher für das Debuggen zu machen.
Um den Fehler zu beheben, müssen Sie den betroffenen Code lokalisieren und die Syntax korrigieren. Mit dieser Aktion wird der Fehler behoben:
>>> len("Pythonista") == 10
True
Jetzt funktioniert der Code ordnungsgemäß und der SyntaxError
ist verschwunden. Ihr Code wird also nicht kaputt gehen und Ihr Programm wird seine normale Ausführung fortsetzen.
Aus dem obigen Beispiel kann man etwas lernen. Sie können Fehler beheben, aber Sie können sie nicht behandeln. Mit anderen Worten: Wenn Sie einen Syntaxfehler wie den im Beispiel haben, können Sie diesen Fehler nicht beheben und den Code nicht ausführen. Sie müssen die Syntax korrigieren.
Andererseits sind Ausnahmen Ereignisse, die die Ausführung eines Programms unterbrechen. Wie der Name schon sagt, treten Ausnahmen in außergewöhnlichen Situationen auf, die passieren sollten oder nicht. Um zu verhindern, dass Ihr Programm nach einer Ausnahme abstürzt, müssen Sie die Ausnahme mit dem entsprechenden Ausnahmebehandlungsmechanismus behandeln.
Um Ausnahmen besser zu verstehen, nehmen Sie an, dass Sie einen Python-Ausdruck wie a + b
haben. Dieser Ausdruck funktioniert, wenn a
und b
beide Zeichenfolgen oder Zahlen sind:
>>> a = 4
>>> b = 3
>>> a + b
7
In diesem Beispiel funktioniert der Code korrekt, da a
und b
beide Zahlen sind. Der Ausdruck löst jedoch eine Ausnahme aus, wenn a
und b
von Typen sind, die nicht addiert werden können:
>>> a = "4"
>>> b = 3
>>> a + b
Traceback (most recent call last):
File "<input>", line 1, in <module>
a + b
~~^~~
TypeError: can only concatenate str (not "int") to str
Da a
eine Zeichenfolge und b
eine Zahl ist, schlägt Ihr Code mit einer TypeError
-Ausnahme fehl. Da es keine Möglichkeit gibt, Text und Zahlen hinzuzufügen, befindet sich Ihr Code in einer Ausnahmesituation.
Python verwendet Klassen zur Darstellung von Ausnahmen und Fehlern. Diese Klassen werden allgemein als Ausnahmen bezeichnet, unabhängig davon, was eine konkrete Klasse darstellt, eine Ausnahme oder einen Fehler. Ausnahmeklassen geben uns Informationen über eine Ausnahmesituation und auch Fehler, die während der Programmausführung erkannt wurden.
Das erste Beispiel in diesem Abschnitt zeigt einen Syntaxfehler in Aktion. Die Klasse SyntaxError
stellt einen Fehler dar, ist jedoch als Python-Ausnahme implementiert. Das könnte verwirrend sein, aber Python verwendet Ausnahmeklassen sowohl für Fehler als auch für Ausnahmen.
Ein weiteres Beispiel für eine Ausnahme könnte sein, dass Sie an einem Code arbeiten, der eine Textdatei verarbeitet, und diese Datei nicht vorhanden ist. In diesem Fall liegt kein Fehler in Ihrem Code vor. Sie haben eine Ausnahmesituation, die Sie bewältigen müssen, um einen Programmabsturz zu verhindern. Sie haben keine Kontrolle über das Problem, da Sie durch Ändern Ihres Codes nicht sicherstellen können, dass die Datei vorhanden ist. Sie müssen die Ausnahme behandeln.
Sie können try
… exclusive
-Blöcke verwenden, um Ausnahmen in Python zu behandeln. Im folgenden Abschnitt lernen Sie die Grundlagen dieser Handhabung kennen.
Umgang mit Ausnahmen
Wenn Sie über einen Code verfügen, der eine Ausnahme auslöst, und Sie keinen Handler-Code für diese Ausnahme bereitstellen, wird die Ausführung Ihres Programms gestoppt. Danach erscheint auf der Standardausgabe, Ihrem Bildschirm, ein Ausnahme-Traceback.
Hinweis: Um die Grundlagen der Ausnahmebehandlung in Python zu erlernen, sehen Sie sich das Tutorial „Python-Ausnahmen: Eine Einführung“ an.
In Python können Sie Ausnahmen mit der Anweisung try
… exclusive
behandeln, die es Ihnen ermöglicht, die Ausnahme abzufangen und rekuperative Aktionen bereitzustellen.
Betrachten Sie das folgende Beispiel. Eine häufige Ausnahme, die Sie sehen werden, wenn Sie beginnen, Pythons Listen und Tupel zu verwenden, ist IndexError
. Diese Ausnahme tritt auf, wenn Sie versuchen, auf einen Index zuzugreifen, der außerhalb des gültigen Bereichs liegt:
>>> numbers = [1, 2, 3]
>>> numbers[5]
Traceback (most recent call last):
...
IndexError: list index out of range
In diesem Beispiel hat die Zahlenliste nur drei Werte. Wenn Sie also versuchen, in einem Indexierungsvorgang auf den Index 5
zuzugreifen, erhalten Sie einen IndexError
, der den Code beschädigt. Sie können diesen Code in einen try
… exclusive
-Block einschließen, um den Ausfall zu verhindern:
>>> try:
... numbers[5]
... except IndexError:
... print("Your list doesn't have that index 😔")
...
Your list doesn't have that index 😔
Nun bricht der Code nicht mit einer Ausnahme ab. Stattdessen wird eine Meldung auf dem Bildschirm ausgegeben. Beachten Sie, dass der Aufruf von print()
lediglich eine Platzhalteraktion für das Beispiel ist. Im realen Code können Sie hier möglicherweise andere Dinge tun.
Das obige Beispiel veranschaulicht das grundlegendste Konstrukt zur Behandlung von Ausnahmen in Python. Sie können sich das oben vorgeschlagene Tutorial ansehen, um tiefer in die Ausnahmebehandlung einzutauchen. Jetzt ist es an der Zeit, die andere Seite der Medaille kennenzulernen. Sie können in Python auch Ausnahmen auslösen.
Ausnahmen auslösen
Python hat die Anweisung raise
als Teil seiner Syntax. Mit dieser Anweisung können Sie in Ihrem Code Ausnahmen als Reaktion auf Ausnahmesituationen auslösen.
Hinweis: Um tiefer in das Auslösen von Ausnahmen in Python einzutauchen, schauen Sie sich das Tutorial Python’s raise: Effectively Raising Exceptions in Your Code an.
Nehmen wir als Beispiel für die Verwendung der raise
-Anweisung an, dass Sie eine Funktion zur Berechnung der Durchschnittsnote von Schülern schreiben müssen. Sie kommen auf die folgende Funktion:
>>> def average_grade(grades):
... return sum(grades) / len(grades)
...
>>> average_grade([5, 4, 5, 3])
4.25
>>> average_grade([])
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
Diese Funktion funktioniert einwandfrei. Wenn die Notenliste jedoch leer ist, erhalten Sie einen Nulldivisionsfehler, da len(grades)
0
sein wird. Wenn der Benutzer des Codes die Fehlermeldung sieht, kann es zu Verwirrung kommen. Ein Nullteilungsfehler? Was verursacht das?
Ein besserer Ansatz wäre wahrscheinlich, sicherzustellen, dass die Eingabeliste nicht leer ist, und in diesem Fall eine geeignetere Ausnahme auszulösen:
>>> def average_grade(grades):
... if not grades:
... raise ValueError("empty grades not allowed")
... return sum(grades) / len(grades)
...
>>> average_grade([5, 4, 5, 3])
4.25
>>> average_grade([])
Traceback (most recent call last):
...
ValueError: empty grades not allowed
In dieser aktualisierten Version von average_grade()
fügen Sie eine bedingte Anweisung hinzu, die prüft, ob die Eingabedaten leer sind. Wenn dies der Fall ist, lösen Sie einen ValueError
mit einer expliziten Fehlermeldung aus, die klar kommuniziert, was mit dem Code nicht stimmt.
Die Ausnahmen IndexError
und ValueError
sind Beispiele für häufig verwendete integrierte Ausnahmen in Python. In den folgenden Abschnitten erfahren Sie mehr über diese und mehrere andere integrierte Ausnahmen.
Dieses Wissen wird Ihnen in mehrfacher Hinsicht helfen. Erstens können Sie schnell herausfinden, welche Art von Fehler möglicherweise in Ihrem Code vorliegt, was Ihre Debugging-Fähigkeiten verbessert. Zweitens verfügen Sie über ein breites Arsenal bereits verfügbarer Ausnahmen, die Sie in Ihrem eigenen Code auslösen können, sodass Sie keine benutzerdefinierten Ausnahmen erstellen müssen.
In Python integrierte Ausnahmen
Python verfügt über über sechzig integrierte Ausnahmen, die eine Vielzahl häufiger Fehler und Ausnahmesituationen darstellen. Diese Ausnahmen sind in zwei Gruppen unterteilt:
- Ausnahmen der Basisklasse
- Konkrete Ausnahmen
Die erste Gruppe von Ausnahmen umfasst Ausnahmeklassen, die hauptsächlich als Basisklassen für andere Ausnahmen dienen. In dieser Gruppe gibt es beispielsweise die Klasse Exception
, die speziell dafür entwickelt wurde, Ihnen das Erstellen benutzerdefinierter Ausnahmen zu ermöglichen.
Die zweite Gruppe enthält Ausnahmen, die Sie häufig in Python-Code sehen oder bei der Ausführung von Python-Code erhalten. Beispielsweise sind Ihnen bei Ihrer täglichen Codierung wahrscheinlich einige der folgenden konkreten Ausnahmen aufgefallen:
ImportError
Erscheint, wenn eine
import
-Anweisung ein Modul nicht laden kannModuleNotFoundError
Tritt auf, wenn
import
ein bestimmtes Modul nicht finden kannNameError
Wird angezeigt, wenn ein Name nicht im globalen oder lokalen Bereich definiert ist
AttributeError
Tritt auf, wenn ein Attributverweis oder eine Attributzuweisung fehlschlägt
IndexError
Tritt auf, wenn ein Indexierungsvorgang für eine Sequenz einen Index außerhalb des gültigen Bereichs verwendet
KeyError
Tritt auf, wenn ein Schlüssel in einem Wörterbuch oder einer anderen Zuordnung fehlt
ZeroDivisionError
Erscheint, wenn der zweite Operand in einer Division oder Modulo-Operation
0
istTypeError
Tritt auf, wenn eine Operation, Funktion oder Methode ein Objekt ungeeigneten Typs bearbeitet
ValueError
Tritt auf, wenn eine Operation, Funktion oder Methode den richtigen Argumenttyp, aber den falschen Wert empfängt
Diese Tabelle ist nur eine kleine Auswahl der in Python integrierten Ausnahmen. Eine umfassende Liste aller integrierten Ausnahmen finden Sie auf der Seite „Integrierte Ausnahmen“ der Python-Dokumentation.
Blick auf die Ausnahmehierarchie
Wie Sie bereits wissen, gibt es in Python viele integrierte Ausnahmen. Sie können sie erkunden, indem Sie den Namespace builtins
in einer REPL-Sitzung untersuchen:
>>> import builtins
>>> dir(builtins)
[
'ArithmeticError',
'AssertionError',
'AttributeError',
'BaseException',
...
]
In diesem Beispiel importieren Sie zunächst den Namespace builtins
. Anschließend verwenden Sie die integrierte Funktion dir()
, um die Namen aufzulisten, die dieses Modul definiert. Beachten Sie, dass Sie die vollständige Liste der integrierten Namen erhalten. Dazwischen finden Sie die integrierten Ausnahmen.
Die in Python integrierten Ausnahmen sind als Klassen codiert und in einer Klassenhierarchie organisiert, die die folgenden Ebenen umfasst:
- Basisklassen: Sie stellen die Basisklassen für andere Ausnahmen bereit. Sie sollten sie nur als übergeordnete Klassen verwenden. In einigen Codebasen finden Sie jedoch möglicherweise einige dieser Ausnahmen, z. B. die Klasse
Exception
. - Konkrete Ausnahmen: Dabei handelt es sich um Ausnahmen, die Python als Reaktion auf verschiedene Ausnahmesituationen auslöst. Sie bieten außerdem eine hervorragende Basis für konkrete Ausnahmen, die Sie bei Bedarf in Ihrem eigenen Code auslösen können.
- Betriebssystemausnahmen: Sie stellen Ausnahmen bereit, die das Betriebssystem generiert. Python gibt sie an Ihre Anwendung weiter. In den meisten Fällen werden Sie diese Ausnahmen abfangen, sie aber nicht in Ihrem Code auslösen.
- Warnungen: Sie geben Warnungen vor unerwarteten Ereignissen oder Aktionen aus, die später zu Fehlern führen können. Diese besonderen Arten von Ausnahmen stellen keine Fehler dar. Wenn Sie sie ignorieren, kann dies später zu Problemen führen, aber Sie können sie ignorieren.
Nachfolgend finden Sie ein Diagramm der Ausnahmehierarchie:
BaseException
├── BaseExceptionGroup
├── GeneratorExit
├── KeyboardInterrupt
├── SystemExit
└── Exception
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ExceptionGroup [BaseExceptionGroup]
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── BlockingIOError
│ ├── ChildProcessError
│ ├── ConnectionError
│ │ ├── BrokenPipeError
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError
│ ├── FileNotFoundError
│ ├── InterruptedError
│ ├── IsADirectoryError
│ ├── NotADirectoryError
│ ├── PermissionError
│ ├── ProcessLookupError
│ └── TimeoutError
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ └── RecursionError
├── StopAsyncIteration
├── StopIteration
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError
│ ├── UnicodeDecodeError
│ ├── UnicodeEncodeError
│ └── UnicodeTranslateError
└── Warning
├── BytesWarning
├── DeprecationWarning
├── EncodingWarning
├── FutureWarning
├── ImportWarning
├── PendingDeprecationWarning
├── ResourceWarning
├── RuntimeWarning
├── SyntaxWarning
├── UnicodeWarning
└── UserWarning
Beachten Sie, dass die meisten Klassen in der Hierarchie von Exception
erben. Dies ist auch die Basisklasse, die Sie in Situationen verwenden sollten, in denen Sie eine benutzerdefinierte Ausnahme erstellen müssen.
Kennen der Basisausnahmen
In der integrierten Ausnahmehierarchie finden Sie einige Klassen, die als Basisklassen konzipiert sind. Die Klasse BaseException
befindet sich oben. Dann haben Sie fünf Unterklassen:
BaseExceptionGroup
Erstellt eine Ausnahmegruppe, die alle Ausnahmen umschließt und nicht nur diejenigen, die von
Exception
erbenGeneratorExit
Tritt auf, wenn ein Generator oder eine Coroutine geschlossen wird
KeyboardInterrupt
Passiert, wenn der Benutzer die Interrupt-Tastenkombination drückt, die normalerweise Strg<span>+C
SystemExit
Ergibt sich aus dem Aufruf der Funktion
sys.exit()
, Sie können ihn aber auch direkt auslösenException
Stellt eine Basisklasse für benutzerdefinierte Ausnahmen bereit, die von dieser Klasse abgeleitet werden sollten
Wie Sie sehen, haben alle diese Basisausnahmen ihren spezifischen Anwendungsfall. Es ist wichtig zu beachten, dass Sie beim Erstellen benutzerdefinierter Ausnahmen Exception
und nicht BaseException
verwenden sollten. Das liegt daran, dass BaseException
für Ausnahmen verwendet werden sollte, die niemals im echten Code abgefangen werden sollten.
In der Praxis sollten Sie beim Abfangen und Auslösen von Ausnahmen die spezifischste Ausnahme für das vorliegende Problem verwenden.
Wenn Sie beispielsweise über einen Code verfügen, der möglicherweise einen ValueError
auslösen kann, sollten Sie diese Ausnahme explizit behandeln. Wenn Sie Exception
anstelle von ValueError
verwenden, fängt Ihr Code Exception
und alle seine Unterklassen, einschließlich ValueError
, ab. Wenn Ihr Code am Ende etwas anderes als ValueError
auslöst, wird dieser Fehler falsch behandelt.
Warnungen kennenlernen
Am Ende der Ausnahmehierarchie finden Sie Warnungen. Hierbei handelt es sich um bestimmte Arten von Ausnahmen, die auf etwas hinweisen, das in naher Zukunft zu Problemen führen kann. Ausnahmen sind Warnungen, die zu den Warnungskategorien gehören.
Hinweis: Da Warnungen eine Art Ausnahme mit spezifischen Anwendungsfällen und spezieller Dokumentation sind, werden sie in diesem Tutorial nicht im Detail behandelt. Eine vollständige Anleitung zu Warnungen finden Sie auf der Seite „Warnungssteuerung“ in der Python-Dokumentation.
Die wahrscheinlich häufigste Warnung, die Sie beim Ausführen von Python-Code sehen, ist DeprecationWarning
. Diese Warnung wird angezeigt, wenn Sie veraltete Funktionen der Sprache verwenden. Beispielsweise sind in Python 3.12 die Konstanten calendar.January
und calendar.February
veraltet:
>>> # Python 3.12.2
>>> calendar.January
<stdin>:1: DeprecationWarning: The 'January' attribute is deprecated,
use 'JANUARY' instead
1
Auch wenn der Code funktioniert, weil die Konstanten noch nicht entfernt wurden, informiert Sie Python darüber, dass die Funktion veraltet ist, um zu verhindern, dass in Zukunft Probleme auftreten. Wie in der Warnmeldung angegeben, sollten Sie jetzt calendar.JANUARY
verwenden.
Syntaxfehler
Die erste Ausnahme, die Sie wahrscheinlich in Python sehen werden, ist die SyntaxError
-Ausnahme. Auch wenn Python für diese Art von Problemen eine Ausnahmeklasse verwendet, sollten Sie sich darüber im Klaren sein, dass es sich dabei eher um Fehler als um Ausnahmen handelt. Wie Sie bereits gelernt haben, kümmern Sie sich also nicht um die Behebung, sondern um die Behebung.
Sie werden außerdem feststellen, dass Python einige zusätzliche Ausnahmen definiert, die von SyntaxError
erben:
IndentationError
TabError
Diese Ausnahmen sind zu erwarten, wenn Sie mit dem Erlernen von Python beginnen, und sie können Sie verwirren. Glücklicherweise verfügen moderne Code-Editoren und IDE über Funktionen, die die Bedingungen, die diese Ausnahmen generieren, erkennen und oft entfernen. In den folgenden Abschnitten erfahren Sie mehr über diese Ausnahmegruppe.
SyntaxError
Wenn Python in einem Codeabschnitt eine ungültige Syntax erkennt, löst es eine SyntaxError
-Ausnahme aus. Die Ausnahme gibt einen Traceback mit hilfreichen Informationen aus, die Sie zum Debuggen des Fehlers und zum Korrigieren Ihres Codes verwenden können.
Hinweis: Weitere Informationen zu Syntaxfehlern finden Sie im Tutorial „Ungültige Syntax in Python: Häufige Gründe für SyntaxError“.
Es gibt viele Situationen, in denen ein Syntaxfehler in Ihrem Code auftreten kann. Sie können ein Komma oder eine schließende Klammer vergessen, einen Operator oder ein Schlüsselwort missbrauchen und vieles mehr.
Hier ein paar Beispiele:
>>> numbers = [1, 2, 3 4]
File "<stdin>", line 1
numbers = [1, 2, 3 4]
^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?
>>> 7 = 7
File "<stdin>", line 1
7 = 7
^
SyntaxError: cannot assign to literal here.
Maybe you meant '==' instead of '='?
>>> class = "Economy"
File "<stdin>", line 1
class = "Economy"
^
SyntaxError: invalid syntax
Dies sind häufige Syntaxfehler, auf die Sie manchmal stoßen können. Die gute Nachricht ist, dass Sie mit den verbesserten Fehlermeldungen, die Python heutzutage bereitstellt, den Fehler schnell aufspüren und beheben können.
IndentationError
Im Gegensatz zu anderen Programmiersprachen ist die Einrückung Teil der Python-Syntax. Um einen Codeblock in Python abzugrenzen, verwenden Sie Einrückungen. Daher erhalten Sie möglicherweise eine Fehlermeldung, wenn die Einrückung nicht korrekt ist. Python verwendet die Ausnahme IndentationError
, um dieses Problem zu kennzeichnen.
Diese Ausnahme kann auftreten, wenn Sie mit dem Erlernen von Python beginnen. Es kann auch auftreten, wenn Sie Code an komplizierten Stellen kopieren und einfügen. Glücklicherweise tritt dieser Fehler heutzutage nicht mehr häufig auf, da moderne Code-Editoren Codeeinrückungen automatisch korrigieren können.
Um die Ausnahme in Aktion zu sehen, betrachten Sie das folgende Beispiel einer Funktion, deren Codeblock eine ungleichmäßige Einrückung verwendet:
>>> def greet(name):
... print(f"Hello, {name}!")
... print("Welcome to Real Python!")
File "<stdin>", line 3
print("Welcome to Real Python!")
^
IndentationError: unindent does not match any outer indentation level
In diesem Beispiel haben die hervorgehobenen Zeilen unterschiedliche Einzüge. Die erste Zeile wird um vier Leerzeichen eingerückt, während die zweite Zeile um zwei Leerzeichen eingerückt wird. Diese Nichtübereinstimmung der Einrückung löst einen IndentationError
aus. Auch hier gilt: Da es sich hierbei um einen Syntaxfehler handelt, meldet Python ihn sofort. Sie können das Problem schnell beheben, indem Sie die Einrückung korrigieren.
TabError
Ein weiteres mögliches Problem für diejenigen, die aus anderen Programmiersprachen kommen, ist die Vermischung von Tabulator- und Leerzeichen beim Einrücken von Python-Code. Die Ausnahme für dieses Problem ist TabError
, eine Unterklasse von IndentationError
. Es handelt sich also um eine weitere Syntaxfehlerausnahme.
Hinweis: Im Style Guide for Python Code (PEP 8) heißt es ausdrücklich:
Leerzeichen sind die bevorzugte Einrückungsmethode. Tabulatoren sollten ausschließlich verwendet werden, um mit Code konsistent zu bleiben, der bereits mit Tabulatoren eingerückt ist. Python verbietet das Mischen von Tabulatoren und Leerzeichen zum Einrücken. (Quelle)
Hier ist ein Beispiel, bei dem Python eine TabError
-Ausnahme auslöst:
def greet(name):
print(f"Hello, {name}!")
print("Welcome to Real Python!")
In diesem Beispiel wird die erste Zeile in greet()
mit einem Tabulatorzeichen eingerückt, während die zweite Zeile mit acht Leerzeichen eingerückt wird, was der üblichen Anzahl von Leerzeichen entspricht, die ein Tabulatorzeichen ersetzt.
Hinweis: Wenn Sie in der zweiten Zeile keine acht Leerzeichen verwenden, erhalten Sie einen IndentationError
anstelle eines TabError
.
Um das Beispiel auszuprobieren, führen Sie die Datei über die Befehlszeile aus:
$ python greeting.py
File ".../greeting.py", line 3
print("Welcome to Real Python!")
TabError: inconsistent use of tabs and spaces in indentation
Da bei der Codeeinrückung Tabulatoren und Leerzeichen gemischt werden, erhalten Sie einen TabError
-Einrückungsfehler. Auch hier sind TabError
-Ausnahmen heutzutage nicht mehr üblich, da moderne Code-Editoren sie automatisch beheben können. Wenn Ihr Editor also gut auf die Python-Entwicklung eingestellt ist, werden Sie diese Ausnahme wahrscheinlich nicht in Aktion sehen.
Importbezogene Ausnahmen
Beim Importieren von Paketen, Modulen und deren Inhalten schlägt Ihr Code manchmal mit der Fehlermeldung fehl, dass die Zieldatei nicht gefunden wurde. In der Praxis kann es zu einer von zwei importbezogenen Ausnahmen kommen:
ModuleNotFoundError
ImportError
Beachten Sie, dass die Ausnahme ModuleNotFoundError
eine Unterklasse von ImportError
mit einer spezifischeren Bedeutung oder einem spezifischeren Ziel ist, wie Sie gleich erfahren werden.
In den folgenden Abschnitten erfahren Sie mehr über diese beiden Ausnahmen und wann Python sie auslöst. Dieses Wissen wird Ihnen helfen, das Problem zu beheben und Ihren Code zum Laufen zu bringen.
ModuleNotFoundError
Wie Sie bereits erfahren haben, ist die Ausnahme ModuleNotFoundError
eine Unterklasse von ImportError
mit einem spezifischeren Ziel. Python löst diese Ausnahme aus, wenn es das Modul, aus dem Sie etwas importieren möchten, nicht finden kann:
>>> import non_existing
Traceback (most recent call last):
...
ModuleNotFoundError: No module named 'non_existing'
Wenn Python das Zielmodul nicht in seinem Importsuchpfad findet, löst es eine ModuleNotFoundError
-Ausnahme aus. Um dieses Problem zu beheben, müssen Sie sicherstellen, dass Ihr Modul in sys.path
aufgeführt ist.
Hinweis: Der beste Weg, um sicherzustellen, dass Ihr Modul im Python-Pfad verfügbar ist, besteht darin, es zu installieren. Sie können pip
verwenden und Ihre lokalen Pakete auf ähnliche Weise installieren, wie Sie Pakete von PyPI installieren.
Da ModuleNotFoundError
eine Unterklasse von ImportError
ist, können Sie, wenn Sie Letzteres explizit in einem try
… exclusive
-Block verwenden Ich werde beide Ausnahmen abfangen. Wenn Sie also Situationen abfangen möchten, in denen das Zielmodul nicht vorhanden ist, sollten Sie konkret sein und ModuleNotFoundError
verwenden.
ImportError
Python löst die ImportError
-Ausnahme für alle importbezogenen Probleme aus, die nicht von ModuleNotFoundError
abgedeckt werden. Dies kann aus zwei Hauptgründen passieren:
- Eine
import module
-Anweisung schlägt aus einem Grund fehl, ein Modul zu laden, der nicht durchModuleNotFoundError
abgedeckt wird. - Eine
from module import name
-Anweisung kannname
nicht im Zielmodul finden.
Fahren Sie nun fort und führen Sie die folgende import
-Anweisung aus, um die ImportError
-Ausnahme in Aktion zu sehen:
>>> from sys import non_existing
Traceback (most recent call last):
...
ImportError: cannot import name 'non_existing' from 'sys' (unknown location)
In diesem Beispiel versuchen Sie, den Namen non_existing
aus dem Modul sys
zu importieren. Da der Name in diesem Modul nicht vorhanden ist, erhalten Sie einen ImportError
. Sie können das Problem schnell beheben, indem Sie den richtigen Zielnamen angeben.
Hinweis: Um tiefer in die Funktionsweise von Importen in Python einzutauchen, schauen Sie sich das Tutorial „Python-Import: Fortgeschrittene Techniken und Tipps“ an.
Im realen Code können Sie ImportError
oder ModuleNotFoundError
nutzen, um je nach Verfügbarkeit der Bibliothek optional verschiedene Module oder Bibliotheken zu laden, die eine bestimmte Funktionalität bereitstellen.
Angenommen, Sie müssen eine TOML-Datei analysieren und ihren Inhalt lesen. In diesem Fall können Sie das Standardbibliotheksmodul tomllib
verwenden, wenn Sie Python 3.11 oder höher verwenden. Andernfalls sollten Sie die Drittanbieterbibliothek tomli
verwenden, die mit tomllib
kompatibel ist.
So können Sie das machen:
try:
import tomllib # Python >= 3.11
except ModuleNotFoundError:
import tomli as tomllib # Python < 3.11
Der Import in der try
-Klausel zielt auf das Standardbibliotheksmodul tomllib
ab. Wenn dieser Import eine Ausnahme auslöst, weil Sie eine Python-Version vor 3.11 verwenden, importiert die exclusive
-Klausel die Drittanbieterbibliothek tomli
, die Sie als installieren müssen eine externe Abhängigkeit Ihres Projekts.
Ausnahmen für Suchfehler
Das Erhalten eines IndexError
beim Durchführen von Indizierungsvorgängen für eine Sequenz oder eines KeyError
beim Nachschlagen von Schlüsseln in Wörterbüchern ist ebenfalls ein häufiges Problem in Python. In den folgenden Abschnitten erfahren Sie mehr über diese beiden Ausnahmen und wann sie in Ihrem Code auftreten können.
IndexError
Die IndexError
-Ausnahme tritt auf, wenn Sie versuchen, einen Wert aus einer Sequenz mithilfe eines Index außerhalb des gültigen Bereichs abzurufen:
>>> colors = [
... "red",
... "orange",
... "yellow",
... "green",
... "blue",
... "indigo",
... "violet",
... ]
>>> colors[10]
Traceback (most recent call last):
File "<input>", line 1, in <module>
colors[10]
~~~~~~^^^^
IndexError: list index out of range
In diesem Beispiel verwenden Sie 10
als Index, um einen Wert aus Ihrer Farben
-Liste abzurufen. Da die Liste nur sieben Elemente enthält, reichen die gültigen Indizes von 0
bis 6
. Ihr Zielindex liegt außerhalb des gültigen Bereichs und Python gibt eine IndexError
-Ausnahme aus.
IndexError
kann in Python eine häufige Ausnahme sein, insbesondere wenn Sie dynamisch generierte Indizes verwenden. Um das Problem zu beheben, müssen Sie herausfinden, welchen Index Ihr Code verwendet, um einen Wert aus der Zielsequenz abzurufen, und dann den Indexbereich anpassen, um das Problem zu beheben.
Hinweis: In den meisten Situationen werden Sie in Python-Schleifen keine Indizes verwenden. Aufgrund dieser Praxis tritt das Indexfehlerproblem in Python-Schleifen weniger häufig auf als in anderen Programmiersprachen, in denen Schleifen auf Indizes basieren.
Wenn Sie den Bereich der Indizes nicht steuern können, können Sie die Ausnahme in einem try
… exclusive
-Block abfangen und die entsprechenden Wiederherstellungsmaßnahmen ergreifen.
Sie können die Ausnahme IndexError
auch in Ihrem eigenen Code auslösen. Diese Ausnahme kann sinnvoll sein, wenn Sie benutzerdefinierte sequenzartige Datenstrukturen erstellen. Angenommen, Sie müssen einen sequenzähnlichen Stapel erstellen:
class Stack:
def __init__(self, items=None):
self.items = list(items) if items is not None else []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def __len__(self):
return len(self.items)
def __getitem__(self, index):
try:
return self.items[index]
except IndexError:
raise IndexError(
f"your stack only has {len(self)} items!"
) from None
In diesem Beispiel definieren Sie Stack
und stellen die Methoden .push()
und .pop()
bereit. Die erstere Methode hängt ein neues Element oben an den Stapel an, während die letztere das Element oben am Stapel entfernt und zurückgibt.
Dann haben Sie die spezielle Methode .__len__()
zur Unterstützung der integrierten Funktion len()
, die Ihnen die Anzahl der Elemente im Stapel liefert.
Schließlich haben Sie die Methode .__getitem__()
. Diese spezielle Methode verwendet einen Index als Argument und gibt das Element am Eingabeindex zurück. Der Block try
… exclusive
fängt die Ausnahme IndexError
ab und löst sie mit einer spezifischeren Fehlermeldung erneut aus. Sie verlassen sich nicht auf die ursprüngliche Nachricht, wodurch Ihr Code fokussierter wird.
KeyError
Wenn Sie versuchen, einen nicht vorhandenen Schlüssel aus einem Wörterbuch abzurufen, erhalten Sie ebenfalls eine KeyError
-Ausnahme. Dieses Problem tritt auch in Python häufig auf:
>>> fruits = {"apple": 0.40, "orange": 0.35, "banana": 0.25}
>>> fruits["grape"]
Traceback (most recent call last):
File "<input>", line 1, in <module>
fruits["grape"]
~~~~~~^^^^^^^^^
KeyError: 'grape'
In diesem Beispiel versuchen Sie, den Schlüssel grape
aus dem Wörterbuch fruits
abzurufen und erhalten eine KeyError
-Ausnahme. In diesem Fall ist die Fehlermeldung ziemlich kurz. Es wird nur der Name des fehlenden Schlüssels angezeigt.
Auch hier kann die Verwendung dynamisch generierter Schlüssel die Ursache des Problems sein. Um das Problem zu beheben, müssen Sie also die generierten und vorhandenen Schlüssel in Ihrem Code überprüfen. Alternativ können Sie die Methode .get()
mit einem entsprechenden Standardwert verwenden, wenn dies eine geeignete Lösung für Ihren Anwendungsfall ist.
Schließlich können Sie auch einen KeyError
in Ihrem Code auslösen. Dies kann hilfreich sein, wenn Sie eine wörterbuchähnliche Klasse erstellen und Ihren Benutzern eine aussagekräftigere Fehlermeldung bereitstellen müssen.
Namensfehler: NameError
Die NameError
-Ausnahme kommt auch recht häufig vor, wenn Sie mit Python beginnen. Diese Ausnahme tritt auf, wenn Sie versuchen, einen Namen zu verwenden, der in Ihrem aktuellen Namespace nicht vorhanden ist. Diese Ausnahme hängt also eng mit Bereichen und Namespaces zusammen.
Angenommen, Sie arbeiten in einer REPL-Sitzung. Sie haben das Modul sys
importiert, um es in Ihrer aktuellen Aufgabe zu verwenden. Aus irgendeinem Grund starten Sie die interaktive Sitzung neu und versuchen, sys
zu verwenden, ohne es erneut zu importieren:
>>> sys.path
Traceback (most recent call last):
File "<input>", line 1, in <module>
sys.path
^^^
NameError: name 'sys' is not defined. Did you forget to import 'sys'?
Da Sie die interaktive Sitzung neu gestartet haben, sind alle Namen und Module, die Sie zuvor importiert haben, verschwunden. Wenn Sie nun versuchen, das Modul sys
zu verwenden, erhalten Sie einen NameError
.
Beachten Sie, dass kein NameError
angezeigt wird, wenn der unbekannte Name in einem qualifizierten Namen hinter dem Punkt steht. In diesem Fall erhalten Sie stattdessen eine AttributeError
-Ausnahme, über die Sie im folgenden Abschnitt mehr erfahren.
Objektbezogene Ausnahmen
Bei der Arbeit mit Python-Klassen, -Objekten und integrierten Typen können einige Ausnahmen auftreten. Die häufigsten sind die folgenden:
TypeError
ValueError
AttributeError
In den folgenden Abschnitten erfahren Sie, wann diese Ausnahmen auftreten und wie Sie in Ihrem Python-Code damit umgehen.
TypeError
Python löst eine TypeError
-Ausnahme aus, wenn Sie eine Operation oder Funktion auf ein Objekt anwenden, das diese Operation nicht unterstützt. Erwägen Sie beispielsweise den Aufruf der integrierten Funktion len()
mit einer Zahl als Argument:
>>> len(42)
Traceback (most recent call last):
File "<input>", line 1, in <module>
len(42)
TypeError: object of type 'int' has no len()
In diesem Beispiel ist das Argument für len()
eine Ganzzahl, die die Funktion nicht unterstützt. Daher erhalten Sie eine Fehlermeldung mit einer entsprechenden Meldung.
In der Praxis können Sie die Ausnahme TypeError
in Ihrem eigenen Code abfangen, wenn Sie ein typbezogenes Problem verhindern müssen. Sie können die Ausnahme auch in Ihrem Code auslösen, um auf ein typbezogenes Problem hinzuweisen. Angenommen, Sie möchten eine Funktion schreiben, die eine Iterable von Zahlen annimmt und eine Liste quadrierter Werte zurückgibt. Sie möchten sicherstellen, dass die Funktion nur iterierbare Objekte akzeptiert.
In dieser Situation können Sie eine Funktion wie die folgende haben:
>>> def squared(numbers):
... try:
... iter(numbers)
... except TypeError:
... raise TypeError(
... f"expected an iterable, got '{type(numbers).__name__}'"
... ) from None
... return [number**2 for number in numbers]
...
>>> squared([1, 2, 3, 4, 5])
[1, 4, 9, 16, 25]
>>> squared(42)
Traceback (most recent call last):
...
TypeError: expected an iterable, got 'int'
In diesem Beispiel verwendet Ihre Funktion die integrierte Funktion iter()
, um die Eingabedaten zu überprüfen. Diese Funktion löst eine TypeError
-Ausnahme aus, wenn die bereitgestellte Eingabe nicht iterierbar ist. Sie fangen diese Ausnahme ab und lösen sie erneut aus, jedoch mit einer benutzerdefinierten Fehlermeldung, die dem Zweck Ihres Codes entspricht. Anschließend erstellen Sie die Liste der quadrierten Werte mithilfe eines Listenverständnisses.
ValueError
In ähnlicher Weise löst Python die Ausnahme ValueError
aus, wenn eine Operation oder Funktion ein Argument erhält, das den richtigen Typ, aber einen ungeeigneten Wert hat:
>>> float("1")
1.0
>>> float("one")
Traceback (most recent call last):
...
ValueError: could not convert string to float: 'one'
In diesem Beispiel verwenden Sie zunächst die integrierte Funktion float()
, um eine Zeichenfolge in eine Gleitkommazahl umzuwandeln. Die Eingabezeichenfolge enthält einen numerischen Wert, sodass der Vorgang erfolgreich ist.
Im zweiten Beispiel rufen Sie dieselbe Funktion mit einer Zeichenfolge auf, die keine gültige Zahl darstellt, und erhalten eine ValueError
-Ausnahme. Beachten Sie, dass das Eingabeargument eine Zeichenfolge ist, was dem richtigen Argumenttyp entspricht. Allerdings ist die Eingabezeichenfolge kein gültiger numerischer Wert, Sie haben also den richtigen Typ, aber den falschen Wert.
Sie können auch ValueError
in Ihrem Python-Code verwenden. Diese Ausnahme kann in verschiedenen Situationen angebracht sein. Angenommen, Sie müssen eine Klasse schreiben, um Regenbogenfarben darzustellen. Diese Klasse verfügt über sieben zulässige Farbnamen, die Sie mit Zeichenfolgen darstellen können:
COLORS = {
"Red": {"Hex": "#FF0000", "RGB": (255, 0, 0)},
"Orange": {"Hex": "#FF7F00", "RGB": (255, 127, 0)},
"Yellow": {"Hex": "#FFFF00", "RGB": (255, 255, 0)},
"Green": {"Hex": "#00FF00", "RGB": (0, 255, 0)},
"Blue": {"Hex": "#0000FF", "RGB": (0, 0, 255)},
"Indigo": {"Hex": "#4B0082", "RGB": (75, 0, 130)},
"Violet": {"Hex": "#8B00FF", "RGB": (139, 0, 255)},
}
class RainbowColor:
def __init__(self, name="Red"):
name = name.title()
if name not in COLORS:
raise ValueError(f"{name} is not a valid rainbow color")
self.name = name
def as_hex(self):
return COLORS[self.name]["Hex"]
def as_rgb(self):
return COLORS[self.name]["RGB"]
In diesem Beispiel definieren Sie zunächst eine COLORS
-Konstante, die die Regenbogenfarben und ihre entsprechende Hex- und RGB-Darstellung enthält.
Anschließend definieren Sie die Klasse RainbowColor
. Im Klasseninitialisierer verwenden Sie eine Bedingung, um sicherzustellen, dass der Name der Eingabefarbe eine der Regenbogenfarben ist. Wenn das nicht der Fall ist, lösen Sie einen ValueError
aus, weil der Farbname vom richtigen Typ ist, aber den falschen Wert hat. Es ist eine Zeichenfolge, aber kein gültiger Farbname.
So funktioniert der Kurs in der Praxis:
>>> from rainbow import RainbowColor
>>> color = RainbowColor("Gray")
Traceback (most recent call last):
...
ValueError: Gray is not a valid rainbow color
>>> color = RainbowColor("Blue")
>>> color.name
'Blue'
>>> color.as_rgb()
(0, 0, 255)
Wenn Sie in diesem Codeausschnitt versuchen, einen nicht zulässigen Farbnamen zu übergeben, erhalten Sie einen ValueError
. Wenn der Farbname gültig ist, funktioniert die Klasse einwandfrei.
AttributeError
Eine weitere häufige Ausnahme, die Sie bei der Arbeit mit Python-Klassen sehen werden, ist AttributeError
. Diese Ausnahme tritt auf, wenn das angegebene Objekt das Attribut oder die Methode, auf die Sie zugreifen möchten, nicht definiert.
Nehmen Sie zum Beispiel Ihre RainbowClass
aus dem vorherigen Abschnitt. Diese Klasse verfügt über ein Attribut und zwei Methoden: .name
, .as_hex()
und .as_rgb()
. Wenn Sie darauf zugreifen, erhalten Sie ein Ergebnis entsprechend ihrer Tätigkeit. Angenommen, Sie versuchen, auf eine Methode namens .as_hsl()
zuzugreifen. Was würde passieren?
Hier ist die Antwort:
>>> from rainbow import RainbowColor
>>> color = RainbowColor("Blue")
>>> color.as_hsl()
Traceback (most recent call last):
...
AttributeError: 'RainbowColor' object has no attribute 'as_hsl'
Da RainbowColor
die Methode .as_hsl()
nicht definiert, erhalten Sie einen AttributeError
, der Ihnen mitteilt, dass die Klasse kein Attribut hat passt zu diesem Namen.
Dieser Fehler kann in komplexen Klassenhierarchien häufig auftreten, die ähnliche Klassen mit leicht unterschiedlichen Schnittstellen definieren. In dieser Situation denken Sie vielleicht, dass eine bestimmte Klasse eine bestimmte Methode oder ein bestimmtes Attribut definiert, aber das ist möglicherweise nicht der Fall. Um das Problem zu beheben, können Sie einen Blick auf die Klassendefinition oder -dokumentation werfen, um sicherzustellen, dass Sie nur die Attribute und Methoden verwenden, die die Klasse definiert.
Sie können die Ausnahme AttributeError
überspringen, indem Sie die integrierte Funktion hasattr()
verwenden:
>>> if hasattr(color, "as_hsl"):
... color.as_hsl()
... else:
... print("Hmm, you can't call that.")
...
Hmm, you can't call that.
In diesem Beispiel verwenden Sie hasattr()
, um explizit zu prüfen, ob das color
-Objekt ein Attribut namens "as_hsl"
hat.
Sie können die Ausnahme AttributeError
auch in Ihren benutzerdefinierten Klassen abfangen und auslösen. In der Praxis kann diese Ausnahme sehr nützlich sein, da Python Sie dazu ermutigt, einen Codierungsstil zu verwenden, der auf der Behandlung von Ausnahmen namens EAFP basiert (es ist einfacher, um Verzeihung als um Erlaubnis zu bitten).
Kurz gesagt bedeutet die Verwendung dieses Stils, dass Sie entscheiden, Fehler und Ausnahmen zu behandeln, wenn sie auftreten. Im Gegensatz dazu versuchen Sie beim LBYL-Stil (Look before you leap), Fehler und Ausnahmen zu verhindern, bevor sie auftreten. Das haben Sie im obigen Beispiel getan.
Anstatt sich also auf hasattr()
zu verlassen, sollten Sie das obige Beispiel wie folgt schreiben:
>>> try:
... color.as_hsl()
... except AttributeError:
... print("Hmm, you can't call that.")
...
Hmm, you can't call that.
Dieser Codierungsstil ist direkter. Sie gehen und rufen die gewünschte Methode auf. Wenn dies mit einem AttributeError
fehlschlägt, führen Sie alternative Aktionen aus.
Schließlich erhalten Sie auch eine AttributeError
-Ausnahme, wenn Sie versuchen, auf ein Objekt zuzugreifen, das nicht in einem bereits importierten Modul definiert ist:
>>> import sys
>>> sys.non_existing
Traceback (most recent call last):
...
AttributeError: module 'sys' has no attribute 'non_existing'
Beachten Sie, dass in diesem Beispiel kein Importfehler angezeigt wird. Stattdessen erhalten Sie eine AttributeError
-Ausnahme. Das liegt daran, dass die in einem Modul definierten Objekte zu Attributen dieses Moduls werden.
Ausnahmen bei arithmetischen Fehlern
Die Ausnahmeklasse ArithmeticError
ist die Basisklasse für integrierte Ausnahmen, die sich auf drei verschiedene Arten von Rechenfehlern beziehen:
ZeroDivisionError
FloatingPointError
OverflowError
Die häufigste dieser drei Ausnahmen ist ZeroDivisionError
. Der FloatingPointError
ist als integrierte Ausnahme definiert, wird aber heutzutage von Python nicht mehr verwendet. In den folgenden Abschnitten erfahren Sie, wann die Ausnahmen ZeroDivisionError
und OverflowError
in Ihrem Code auftreten können.
ZeroDivisionError
Python löst die Ausnahme ZeroDivisionError
aus, wenn der Divisor oder rechte Operand einer Division Null ist. Nehmen wir als kurzes Beispiel an, dass Sie eine Funktion schreiben müssen, um die Durchschnittsnote einer Gruppe von Schülern zu berechnen. In diesem Fall können Sie sich die folgende Funktion ausdenken:
>>> def average_grade(grades):
... return sum(grades) / len(grades)
...
>>> average_grade([4, 3, 3, 4, 5])
3.8
Diese Funktion funktioniert einwandfrei, wenn Sie geeignete Eingabedaten verwenden. Aber was würde passieren, wenn Sie die Funktion mit einem leeren Listenobjekt aufrufen würden? Hier ist die Antwort:
>>> average_grade([])
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
Da die Eingabeliste leer ist, gibt die Funktion len()
0
zurück. Dieser Rückgabewert erzeugt eine ZeroDivisionError
-Ausnahme, wenn die tatsächliche Division erfolgt.
Um das Problem zu beheben, können Sie die Ausnahme abfangen und dem Benutzer eine beschreibende Fehlermeldung anzeigen:
>>> def average_grade(grades):
... try:
... return sum(grades) / len(grades)
... except ZeroDivisionError:
... raise ValueError(
... "you should pass at least one grade to average_grade()"
... ) from None
...
>>> average_grade([])
Traceback (most recent call last):
...
ValueError: you should pass at least one grade to average_grade()
In dieser aktualisierten Version von average_grade()
fangen Sie den ZeroDivisionError
ab, den eine leere Eingabeliste generiert, und lösen einen ValueError
mit einem benutzerfreundlichen Fehler aus Nachricht.
Schließlich tritt die ZeroDivisionError
-Ausnahme auch auf, wenn der rechte Operand einer Modulo-Operation Null ist:
>>> 42 % 0
Traceback (most recent call last):
File "<input>", line 1, in <module>
42 % 0
~~~^~~
ZeroDivisionError: integer modulo by zero
Der Modulo-Operator gibt den Rest einer Division zweier Zahlen zurück. Da die Division der Zahlen der erste Schritt zur Berechnung des Restes ist, können Sie einen ZeroDivisionError
erhalten, wenn der rechte Operand 0
ist.
OverflowError
Der OverflowError
kommt nicht so häufig vor. Python löst diese Ausnahme aus, wenn das Ergebnis einer arithmetischen Operation zu groß ist und es keine Möglichkeit gibt, es darzustellen:
>>> 10.0**1000
Traceback (most recent call last):
File "<input>", line 1, in <module>
10.0**1000
~~~~^^~~~~
OverflowError: (34, 'Result too large')
In diesem Beispiel ist die Zahl, die sich aus der Power-Operation ergibt, zu hoch und Python kann sie nicht korrekt darstellen. Sie erhalten also eine OverflowError
-Ausnahme.
Iterationsbezogene Ausnahmen
Python verfügt über einige integrierte Ausnahmen, die eng mit der Iteration zusammenhängen. Der RecursionError
bezieht sich auf rekursiven Code und kann als Standardausnahme bezeichnet werden. Dann gibt es die Ausnahme StopIteration
, die Python intern verwendet, um den Ausführungsfluss von for
-Schleifen zu steuern. Diese Ausnahme hat ein asynchrones Gegenstück namens AsyncStopIteration
, das Python in async for
-Schleifen verwendet.
Hinweis: Die Ausnahme AsyncStopIteration
hat dieselbe semantische Bedeutung wie die Ausnahme StopIteration
, jedoch für asynchrone Schleifen. Daher wird diese Ausnahme in diesem Tutorial nicht behandelt.
In den folgenden Abschnitten erfahren Sie mehr über die Ausnahmen RecursionError
und StopIteration
und wie Python sie verwendet.
RecursionError
Eine Funktion, die sich selbst aufruft, wird als rekursive Funktion bezeichnet. Diese Art von Funktionen bilden die Grundlage einer Iterationstechnik namens Rekursion.
Hinweis: Weitere Informationen zur Rekursion finden Sie im Tutorial „Rekursion in Python: Eine Einführung“.
Betrachten Sie die folgende Spielzeugfunktion als Beispiel für eine rekursive Funktion:
>>> def recurse():
... recurse()
...
Diese Funktion folgt nicht den Rekursionsregeln, da sie keinen Basisfall hat, der die Wiederholung stoppt. Es gibt nur einen rekursiven Fall. Wenn Sie also die Funktion aufrufen, erhalten Sie eine RecursionError
-Ausnahme:
>>> recurse()
Traceback (most recent call last):
File "<input>", line 1, in <module>
recurse()
File "<input>", line 2, in recurse
recurse()
File "<input>", line 2, in recurse
recurse()
File "<input>", line 2, in recurse
recurse()
[Previous line repeated 981 more times]
RecursionError: maximum recursion depth exceeded
Python löst eine RecursionError
-Ausnahme aus, wenn der Interpreter erkennt, dass die maximale Rekursionstiefe überschritten wird. Dies kann passieren, wenn Ihr Basisfall nicht ordnungsgemäß funktioniert oder wenn die erforderliche Anzahl von Rekursionen zur Durchführung der Berechnung das Rekursionslimit überschreitet.
Hinweis: Ein Rekursionslimit ist erforderlich, da jeder Funktionsaufruf Speicher im Aufrufstapel Ihres Systems belegt. Dies ist also eine Möglichkeit, die Speichernutzung zu steuern.
Sie können die Funktion sys.getrecursionlimit()
verwenden, um Ihre aktuelle Rekursionstiefe zu überprüfen:
>>> import sys
>>> sys.getrecursionlimit()
1000
Das Ergebnis des Aufrufs dieser Funktion ist eine Zahl, die angibt, wie oft sich eine rekursive Funktion in Python selbst aufrufen kann. Wenn es für Ihren Anwendungsfall angemessen ist, können Sie mit der Funktion sys.setrecursionlimit()
auch ein anderes Rekursionslimit festlegen.
StopIteration
Python löst eine StopIteration
-Ausnahme aus, wenn Sie die integrierte Funktion next()
für einen erschöpften Iterator aufrufen. Sein Zweck besteht darin, zu signalisieren, dass sich keine weiteren Elemente im Iterator befinden. Daher verwendet Python diese Ausnahme intern, um die Iteration zu steuern.
Betrachten Sie das folgende Spielzeugbeispiel:
>>> numbers_iterator = iter([1, 2, 3])
>>> next(numbers_iterator)
1
>>> next(numbers_iterator)
2
>>> next(numbers_iterator)
3
>>> next(numbers_iterator)
Traceback (most recent call last):
File "<input>", line 1, in <module>
next(numbers_iterator)
StopIteration
In diesem Beispiel verwenden Sie die integrierte Funktion iter()
, um einen Iterator aus einer kurzen Liste von Zahlen zu erstellen. Anschließend rufen Sie wiederholt next()
auf, um den Iterator zu nutzen. Der vierte Aufruf dieser Funktion löst eine StopIteration
-Ausnahme aus, um zu signalisieren, dass der Iterator keine weiteren Elemente zum Abrufen hat.
Intern verwendet Python diese Ausnahme, um for
-Schleifen zu beenden. Mit anderen Worten: Wenn eine for
-Schleife über einen Iterator iteriert, stoppt die Schleife, wenn sie die Ausnahme StopIteration
erhält. Aus diesem Grund ist es nicht üblich, diese Ausnahme in Aktion zu sehen. Beachten Sie, dass die for
-Schleife von Python unter der Haube next()
aufruft, um die Iteration durchzuführen.
In der Praxis können Sie die Ausnahme StopIteration
in Ihrem Code als Teil einer Implementierung des Iteratorprotokolls verwenden. Dieses Protokoll besteht aus zwei speziellen Methoden:
.__iter__()
wird aufgerufen, um den Iterator zu initialisieren. Es muss ein Iteratorobjekt zurückgeben..__next__()
wird aufgerufen, um den Iterator zu durchlaufen. Es muss den nächsten Wert im Datenstrom zurückgeben.
Wenn Sie diese Methoden in einer benutzerdefinierten Klasse bereitstellen, unterstützt Ihre Klasse die Iteration. Um zu signalisieren, wann die Iteration beendet werden muss, müssen Sie die Ausnahme StopIteration
in der Methode .__next__()
auslösen.
Hinweis: Weitere Informationen zu Iteratoren in Python finden Sie im Tutorial „Iteratoren und Iterables in Python: Effiziente Iterationen ausführen“.
Um die Verwendung von StopIteration
zu veranschaulichen, nehmen wir an, dass Sie eine benutzerdefinierte Klasse erstellen müssen, die eine Liste von Werten übernimmt und einen Iterator über deren Quadrate erstellt:
class SquareIterator:
def __init__(self, values):
self.values = values
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.values):
raise StopIteration
square = self.values[self.index] ** 2
self.index += 1
return square
In dieser Klasse verwendet der Initialisierer eine Liste von Werten als Argument. Außerdem wird ein Referenzindex definiert, der bei 0
beginnt. Die Methode .__iter__()
gibt self
zurück, was üblich ist, wenn Sie das Iterator-Protokoll wie in diesem Beispiel verwenden.
In der Methode .__next__()
prüfen Sie, ob der aktuelle Index größer oder gleich der Anzahl der Werte in der Eingabeliste ist. Wenn dies der Fall ist, lösen Sie eine StopIteration
aus, um zu signalisieren, dass der Iterator erschöpft ist. Als nächstes berechnen Sie das Quadrat des aktuellen Werts und geben es als Ergebnis der Methode zurück.
Jetzt können Sie Instanzen dieser Klasse in einer for
-Schleife verwenden:
>>> from squares import SquareIterator
>>> for value in SquareIterator([1, 2, 3]):
... print(value)
...
1
4
9
Ihr Iterator funktioniert gut. Es generiert quadratische Werte und unterstützt die Iteration. Beachten Sie, dass die Ausnahme StopIteration
nicht auftritt. Allerdings verwendet Python die Ausnahme intern, um die Schleife zu beenden, wenn der Iterator fertig ist.
Dateibezogene Ausnahmen
Bei der Verarbeitung von Dateien in Ihrem Python-Code können verschiedene Probleme auftreten. Ihr Code versucht möglicherweise, eine bereits vorhandene Datei zu erstellen, auf eine nicht vorhandene Datei zuzugreifen, einen Dateivorgang für ein Verzeichnis statt für eine Datei auszuführen oder es treten Berechtigungsprobleme auf. Python verfügt über dateibezogene Ausnahmen, die diese Situationen kennzeichnen.
Hier sind einige der beliebtesten:
FileExistsError
FileNotFoundError
PermissionError
In den folgenden Abschnitten erfahren Sie mehr über diese integrierten Ausnahmen und wann sie in Python auftreten.
FileExistsError
Wenn Sie versuchen, eine bereits vorhandene Datei oder ein Verzeichnis zu erstellen, löst Python die integrierte Ausnahme FileExistsError
aus.
Um diese Ausnahme in Aktion zu sehen, müssen Sie beispielsweise eine Datei in Ihrem Arbeitsverzeichnis erstellen und Text hineinschreiben. Sie möchten jedoch vermeiden, die Datei zu ersetzen, wenn sie bereits vorhanden ist. In diesem Fall können Sie den FileExistsError
nutzen und den folgenden Code schreiben:
try:
with open("hello.txt", mode="x") as file:
file.write("Hello, World!")
except FileExistsError:
print("The file already exists")
In diesem Beispiel verwenden Sie die integrierte Funktion open()
, um eine Datei namens hello.txt
zu öffnen. Der "x"
-Modus bedeutet, dass Sie die Datei zur exklusiven Erstellung öffnen möchten, was fehlschlägt, wenn die Datei bereits vorhanden ist.
Fahren Sie nun fort und führen Sie diese hello.py
-Datei über Ihre Befehlszeile aus:
$ python hello.py
Dieser Befehl gibt keine Ausgabe aus. Wenn Sie sich jedoch Ihr Arbeitsverzeichnis ansehen, sehen Sie eine neue Datei namens hello.txt
mit dem Text "Hello, World!"
darin.
Wenn Sie den Befehl erneut ausführen, ist das Ergebnis anders:
$ python hello.py
The file already exists
Da die Datei hello.txt
dieses Mal bereits vorhanden ist, löst der Dateierstellungscode eine FileExistsError
-Ausnahme aus und die exclusive
-Klausel gibt einen Fehler aus Meldung auf dem Bildschirm.
FileNotFoundError
Python löst die Ausnahme FileNotFoundError
aus, wenn eine Datei oder ein Verzeichnis angefordert wird, aber am Zielspeicherort nicht vorhanden ist. Angenommen, Sie haben versehentlich die Datei hello.txt
entfernt, die Sie im vorherigen Abschnitt erstellt haben. Wenn Sie versuchen, den Inhalt der Datei zu lesen, erhalten Sie eine Fehlermeldung:
>>> with open("hello.txt", mode="r") as file:
... print(file.read())
...
Traceback (most recent call last):
File "<input>", line 1, in <module>
with open("hello.txt", mode="r") as file:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'hello.txt'
In diesem Codeausschnitt versuchen Sie, die Datei hello.txt
zu öffnen, um ihren Inhalt zu lesen. Da Sie die Datei vor diesem Vorgang entfernt haben, schlägt Ihr Code mit der Ausnahme FileNotFoundError
fehl.
Um dieses Problem zu lösen, können Sie Ihren Code wie im folgenden Beispiel umschreiben:
>>> try:
... with open("hello.txt", mode="r") as file:
... print(file.read())
... except FileNotFoundError:
... print("The file doesn't exist")
...
The file doesn't exist
Jetzt verwenden Sie einen try
… exclusive
-Block, um die FileNotFoundError
-Ausnahme zu behandeln und zu verhindern, dass Ihr Code ausbricht.
PermissionError
Ein weiteres häufiges Problem bei der Verarbeitung von Dateien besteht darin, dass Sie versuchen, ohne entsprechende Zugriffsberechtigungen auf eine Datei oder ein Verzeichnis zuzugreifen. In diesem Fall löst Python die Ausnahme PermissionError
aus, um Sie darüber zu informieren, dass Sie nicht über die erforderlichen Berechtigungen verfügen.
Angenommen, Sie arbeiten auf einem Unix-ähnlichen System wie Linux oder macOS. Sie möchten den Inhalt der Datei /etc/hosts
aktualisieren. In Unix-ähnlichen Systemen kann nur der Root-Benutzer diese Datei ändern. Wenn Sie also versuchen, Ihre Aufgabe mit einem anderen Benutzer auszuführen, erhalten Sie eine Fehlermeldung:
>>> with open("/etc/hosts", mode="a") as file:
... print(file.write("##"))
...
Traceback (most recent call last):
File "<input>", line 1, in <module>
with open("/etc/hosts", mode="a") as file:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: '/etc/host'
Dieser Fehler tritt auf, weil die Datei /etc/hosts
eine Systemdatei ist. Sie benötigen Root-Rechte, um diese Datei zu ändern. Ihr Code versucht, die Datei zum Anhängen von Inhalten zu öffnen, ohne über die erforderlichen Berechtigungen zu verfügen, was zu einer PermissionError
-Ausnahme führt.
Abstrakte klassenbezogene Ausnahme: NotImplementedError
Manchmal möchten Sie eine benutzerdefinierte Basisklasse mit einer vordefinierten Schnittstelle erstellen, von der Ihre Benutzer ihre eigenen Klassen ableiten können. Auf diese Weise stellen Sie sicher, dass die Unterklassen die erforderliche Schnittstelle erfüllen. In Python können Sie dies mithilfe einer sogenannten abstrakten Basisklasse (ABC) tun.
Das Modul abc
in der Standardbibliothek stellt die Klasse ABC
und andere verwandte Tools bereit, mit denen Sie benutzerdefinierte Basisklassen definieren können, die eine bestimmte Schnittstelle definieren. Beachten Sie, dass Sie ABCs nicht direkt instanziieren können. Sie sollen in Unterklassen unterteilt werden.
Wenn Sie mit der Erstellung Ihrer ABCs beginnen, werden Sie feststellen, dass es beim Definieren von Methoden üblich ist, eine NotImplementedError
-Ausnahme für die Methoden auszulösen, die die Unterklassen implementieren sollten, aber keine strenge Anforderung darstellen.
Angenommen, Sie möchten eine Klassenhierarchie erstellen, um verschiedene Vögel darzustellen, beispielsweise eine Ente, einen Schwan, einen Pinguin usw. Sie entscheiden, dass alle Klassen über die Methoden .swim()
und .fly()
verfügen sollen. In dieser Situation können Sie mit der folgenden Basisklasse beginnen:
from abc import ABC
class Bird(ABC):
def swim(self):
raise NotImplementedError("must be implemented in subclasses")
def fly(self):
raise NotImplementedError("must be implemented in subclasses")
In dieser Bird
-Klasse erben Sie von abc.ABC
, was bedeutet, dass Sie eine abstrakte Basisklasse erstellen. Anschließend definieren Sie die Methoden .swim()
und .fly()
, die die Ausnahme NotImplementedError
mit einer entsprechenden Fehlermeldung auslösen.
Hinweis: Sie können auch abstractmethod
aus abc
importieren und .swim()
und .fly( dekorieren. )
mit @abstractmethod
. Python löst einen Fehler aus, wenn Sie eine Klasse mit einer abstrakten Methode instanziieren.
Hier ist ein Beispiel dafür, wie Sie konkrete Vögel aus der oben genannten Klasse ableiten können:
# ...
class Duck(Bird):
def swim(self):
print("The duck is swimming")
def fly(self):
print("The duck is flying")
class Penguin(Bird):
def swim(self):
print("The penguin is swimming")
In diesem Beispiel erstellen Sie die Klasse Duck
mit den Methoden .swim()
und .fly()
. Anschließend erstellen Sie die Klasse Penguin
, die nur über die Methode .swim()
verfügt, da Pinguine nicht fliegen können. Sie können die Klasse Duck
normal verwenden. Im Gegensatz dazu verhält sich die Klasse Penguin
anders, wenn Sie ihre Methode .fly()
aufrufen:
>>> from birds import Duck
>>> donald = Duck()
>>> donald.swim()
The duck is swimming
>>> donald.fly()
The duck is flying
>>> skipper = Penguin()
>>> skipper.swim()
The penguin is swimming
>>> skipper.fly()
Traceback (most recent call last):
...
NotImplementedError: must be implemented in subclasses
In diesem Codeausschnitt funktioniert die Klasse Duck
wie erwartet. Unterdessen löst die Klasse Penguin
einen NotImplementedError
aus, wenn Sie die Methode .fly()
für eine ihrer Instanzen aufrufen.
Assertionsfehler: AssertionError
Python verfügt über eine Funktion namens Assertions, die Sie mit der Anweisung assert
definieren können. Mit Behauptungen können Sie während der Entwicklung Ihres Codes Zulässigkeitsprüfungen festlegen. Mithilfe von Behauptungen können Sie die Korrektheit Ihres Codes testen, indem Sie prüfen, ob bestimmte Bedingungen weiterhin erfüllt sind. Dies ist praktisch, wenn Sie Ihren Code testen und debuggen.
Hinweis: Um tiefer in die assert
-Anweisung einzutauchen, sehen Sie sich das Tutorial „Python‘s Assert: Debug and Test Your Code Like a Pro“ an.
Die Assert-Anweisung hat die folgende Syntax:
assert expression[, assertion_message]
Der Teil expression
ist eine Bedingung, die wahr sein sollte, es sei denn, Ihr Programm weist einen Fehler auf. Wenn die Bedingung falsch wird, löst die Behauptung eine AssertionError
-Ausnahme aus und beendet die Ausführung Ihres Programms.
Behauptungen sind nützlich zum Schreiben von Testfällen. Sie können sie verwenden, um Annahmen wie Vorbedingungen und Nachbedingungen zu überprüfen. Sie können beispielsweise testen, ob ein Argument von einem bestimmten Typ ist, Sie können den Rückgabewert einer Funktion testen und so weiter. Diese Prüfungen können Ihnen dabei helfen, Fehler bei der Entwicklung eines Programms so schnell wie möglich zu erkennen.
Hinweis: Sie sollten sich nicht auf Zusicherungen verlassen, um Annahmen im Produktionscode zu überprüfen, da Zusicherungen deaktiviert sind, wenn Ihr Code im optimierten Modus mit Pythons -O
ausgeführt wird. oder -OO
Befehlszeilenoptionen.
Angenommen, Sie befinden sich in einem Interview zum Python-Programmieren. Sie werden gebeten, eine Funktion zu implementieren, die die FizzBuzz-Herausforderung meistert, wobei Sie "fizz"
für durch drei teilbare Zahlen, "buzz"
für durch fünf teilbare Zahlen und zurückgeben "fizz Buzz"
für diejenigen, die sowohl durch drei als auch durch fünf teilbar sind. Sie schreiben eine Funktion wie die folgende:
def fizzbuzz(number):
if number % 3 == 0:
return "fizz"
elif number % 5 == 0:
return "buzz"
elif number % 15 == 0:
return "fizz buzz"
else:
return number
Diese Funktion deckt offenbar alle möglichen Szenarien ab. Sie beschließen jedoch, einige grundlegende Tests zu schreiben, um sicherzustellen, dass die Funktion ordnungsgemäß funktioniert. Am Ende Ihrer fizzbuzz.py
-Datei erhalten Sie den folgenden Code:
# ...
if __name__ == "__main__":
assert fizzbuzz(9) == "fizz"
assert fizzbuzz(10) == "buzz"
assert fizzbuzz(15) == "fizz buzz"
assert fizzbuzz(7) == 7
print("All tests pass")
In diesem Codeausschnitt haben Sie einige schnelle Behauptungen hinzugefügt, um zu überprüfen, ob Ihre Funktion bei unterschiedlichen Eingabewerten die richtige Zeichenfolge zurückgibt. Jetzt können Sie den Test ausführen, indem Sie die Datei über die Befehlszeile ausführen:
$ python fizzbuzz.py
Traceback (most recent call last):
File ".../fizzbuzz.py", line 26, in <module>
assert fizzbuzz(15) == "fizz buzz"
^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
Wow! Sie haben eine AssertionError
-Ausnahme erhalten, was bedeutet, dass einige der Tests fehlgeschlagen sind. Wenn Sie sich den Ausnahme-Traceback ansehen, stellen Sie fest, dass die Behauptung fehlschlägt, wenn Sie die Funktion mit 15
als Argument aufrufen. Diese Zahl ist durch 3
und durch 5
teilbar, daher ist die aktuelle Reihenfolge Ihrer Bedingungen falsch.
Sie müssen die Bedingung Nummer % 15 == 0
an die erste Position verschieben:
def fizzbuzz(number):
if number % 15 == 0:
return "fizz buzz"
elif number % 3 == 0:
return "fizz"
elif number % 5 == 0:
return "buzz"
else:
return number
# ...
Die Bedingungen prüfen zunächst, ob die eingegebene Zahl durch 3
und 5
teilbar ist. Fahren Sie nun fort und führen Sie die Datei erneut aus:
$ python fizzbuzz.py
All tests pass
Großartig! Alle Ihre Tests bestehen. Die Funktion erledigt ihre Aufgabe korrekt. Das ist die Stärke der assert
-Anweisung.
Beachten Sie abschließend, dass Sie die Ausnahme AssertionError
in Ihrem Code nicht explizit auslösen sollten. Stattdessen sollten Sie zulassen, dass die Anweisung assert
diese Ausnahme auslöst, wenn die Bedingung der Behauptung fehlschlägt. Darüber hinaus sollten Sie nicht versuchen, Fehler zu behandeln, indem Sie Code schreiben, der die Ausnahme AssertionError
abfängt, da Assertionen deaktiviert werden können.
Interpreter-Exit-Ausnahmen
Beim Schreiben von Python-Code werden Sie auf Situationen stoßen, in denen Sie eine laufende Anwendung oder ein laufendes Programm beenden müssen.
Wenn beispielsweise während der Ausführung einer App ein Fehler auftritt, können Sie entscheiden, die App sauber mit einem entsprechenden Exit-Status zu beenden, was bei Befehlszeilen-Apps hilfreich sein kann. Ein weiteres Beispiel wäre, wenn Sie Code testen, dessen Ausführung zu lange dauert oder der irgendwie hängen bleibt. In diesem Fall möchten Sie die Ausführung des Codes schnell beenden.
Python verfügt über die folgenden Ausnahmen, die diese Situationen behandeln:
SystemExit
KeyboardInterrupt
Im Allgemeinen sollte Ihr Code diese Ausnahmen nicht abfangen oder behandeln. Dies kann dazu führen, dass Sie Ihr Programm nicht beenden können, sodass es unmöglich ist, es aus dem Code heraus oder über die Tastatur zu beenden.
Hinweis: Das plötzliche Beenden eines Programms mit einem SystemExit
oder KeyboardInterrupt
kann zu unerwünschten, chaotischen Zuständen in Ihrem System führen, wie etwa übrig gebliebene temporäre Dateien und ein offenes Netzwerk oder Datenbankverbindungen.
Sie können das Modul atexit
aus der Standardbibliothek verwenden, um zu verwalten, wie Ihr Code auf diese Vorgehensweise reagiert. Mit diesem Modul können Sie Bereinigungsfunktionen registrieren und die Registrierung aufheben, die bei normaler Beendigung des Python-Interpreters automatisch ausgeführt werden.
In den folgenden Abschnitten erfahren Sie, wann diese Ausnahmen in Ihrem Python-Code auftreten können. Sie werden auch einige Beispiele schreiben, die ihre Verwendung veranschaulichen.
SystemExit
Python löst die Ausnahme SystemExit
aus, wenn Sie die Funktion sys.exit()
in Ihrem Code aufrufen. Wenn Sie die Ausnahme nicht behandeln, wird der Python-Interpreter beendet, ohne einen Ausnahme-Traceback auszugeben:
>>> import sys
>>> sys.exit(0)
$
In diesem Beispiel rufen Sie sys.exit()
auf, um den Python-Interpreter zu beenden. Dieser Anruf führt Sie zurück zu Ihrer Terminalsitzung.
In der Praxis können Sie SystemExit
direkt verwenden, wenn Sie eine App beenden müssen. Angenommen, Sie möchten eine minimale CLI-App (Befehlszeilenschnittstelle) erstellen, die die Grundfunktionalität des Unix-Befehls ls
nachahmt, der den Inhalt eines bestimmten Verzeichnisses auflistet.
In dieser Situation können Sie ein Skript wie das folgende schreiben:
import sys
from pathlib import Path
if (args_count := len(sys.argv)) > 2:
print(f"One argument expected, got {args_count - 1}")
raise SystemExit(2)
elif args_count < 2:
print("You must specify the target directory")
raise SystemExit(2)
target_dir = Path(sys.argv[1])
if not target_dir.is_dir():
print("The target directory doesn't exist")
raise SystemExit(1)
for entry in target_dir.iterdir():
print(entry.name)
Dieses Programm verarbeitet die in der Befehlszeile bereitgestellten Argumente, die automatisch in der Variablen sys.argv
gespeichert werden. Das erste Element in sys.argv
ist der Name des Programms. Das zweite Element sollte das Zielverzeichnis sein.
Die App sollte nur ein Zielverzeichnis akzeptieren, daher darf die Variable args_count
höchstens 2
sein. Wenn die App mehr als ein Zielverzeichnis erhält, geben Sie eine Fehlermeldung aus und lösen eine SystemExit
-Ausnahme mit dem Exit-Status 2
aus. Dies weist darauf hin, dass die App nach einem Fehler beendet wurde.
Der Zweig elif
prüft, ob der Benutzer ein Zielverzeichnis angegeben hat. Ist dies nicht der Fall, geben Sie dem Benutzer eine Fehlermeldung aus und beenden die App, wodurch eine SystemExit
-Ausnahme ausgelöst wird.
Nachdem Sie den Inhalt von sys.argv
überprüft haben, erstellen Sie ein pathlib.Path
-Objekt, um den Pfad zu Ihrem Zielverzeichnis zu speichern. Wenn dieses Verzeichnis nicht existiert, informieren Sie den Benutzer und beenden die App erneut mit der Ausnahme SystemExit
. Diesmal ist der Exit-Status 1
, um zu signalisieren, dass bei der Ausführung der App ein Fehler aufgetreten ist. Schließlich listet die for
-Schleife den Verzeichnisinhalt auf, einen Eintrag pro Zeile.
Auf einem Unix-ähnlichen System wie Linux und macOS können Sie den folgenden Befehl ausführen, um zu überprüfen, wie das Skript funktioniert:
$ python ls.py
You must specify the target directory
$ echo $?
2
$ python ls.py /home /etc
One argument expected, got 2
$ echo $?
2
$ python ls.py .
hello.py
birds.py
grades.py
greeting.py
fizzbuzz.py
ls.py
mean.py
stack.py
square.py
rainbow.py
$ echo $?
0
Wenn Sie das Skript ohne Argument ausführen, erhalten Sie eine Meldung, dass Sie ein Zielverzeichnis angeben müssen. Wenn Sie den Befehl echo
verwenden, um den Exit-Code, dargestellt durch $?
, zu überprüfen, werden Sie sehen, dass es sich um 2
handelt. Im zweiten Beispiel geben Sie zwei Zielverzeichnisse an. Auch hier schlägt die App mit einer Fehlermeldung fehl. Der Befehl echo
gibt auch 2
zurück.
Abschließend führen Sie das Skript mit dem aktuellen Arbeitsverzeichnis als Argument aus. In diesem Fall erhalten Sie die Liste der Dateien, die sich in diesem Verzeichnis befinden. Wenn Sie echo
ausführen, erhalten Sie den Exit-Status 0
, der signalisiert, dass die Ausführung der App erfolgreich war.
KeyboardInterrupt
Die KeyboardInterrupt
-Ausnahme unterscheidet sich etwas von anderen Ausnahmen. Python löst diese Ausnahme aus, wenn der Benutzer absichtlich die Tastenkombination Strg<span>+C drückt während der Ausführung eines Programms. Diese Aktion unterbricht ein laufendes Programm.
Angenommen, Sie arbeiten an einem Codeabschnitt, der eine Schleife enthält. Aus Versehen gerät der Code in eine Endlosschleife. In dieser Situation benötigen Sie eine schnelle Möglichkeit, die Ausführung des Codes zu beenden. Dann ist die Tastenkombination Strg<span>+C praktisch.
Betrachten Sie die folgende Spielzeugschleife:
>>> while True:
... print("Hello")
...
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Traceback (most recent call last):
...
KeyboardInterrupt
Diese Schleife ist absichtlich so geschrieben, dass sie unendlich ist. In der Praxis kann es zu echten Schleifen kommen, die aufgrund eines logischen Fehlers in eine unendliche Iteration übergehen. Wenn dieses Problem auftritt, können Sie die Tasten Strg<span>+C drücken Beenden Sie die Ausführung des Codes. Infolgedessen löst Python eine KeyboardInterrupt
-Ausnahme aus.
Abschließend ist es wichtig zu beachten, dass Sie diese Ausnahme nicht in Ihrem Code abfangen sollten, da dies dazu führen kann, dass der Interpreter nicht beendet wird.
Ausnahmegruppen
In Python 3.11 und höher gibt es die Klassen ExceptionGroup
und BaseExceptionGroup
. Sie können sie verwenden, wenn Sie mehrere unabhängige Ausnahmen gleichzeitig auslösen müssen. Der Unterschied zwischen diesen Klassen besteht darin, dass BaseExceptionGroup
BaseException
erweitert, während ExceptionGroup
Exception
erweitert.
Hinweis: Weitere Informationen zu Ausnahmegruppen finden Sie im Tutorial „Python 3.11 Preview: Task and Exception Groups“.
Sie können auf diese Ausnahmen zurückgreifen, wenn ein asynchrones Programm mehrere gleichzeitige Aufgaben hat, die gleichzeitig fehlschlagen könnten. Aber im Allgemeinen werden Sie eine ExceptionGroup
sparsam auslösen.
Sie können Ausnahmegruppen nach Bedarf bearbeiten und auslösen. So fangen Sie eine Ausnahmegruppe mit der zugehörigen exclusive*
-Syntax ab:
>>> try:
... raise ExceptionGroup(
... "several exceptions",
... [
... ValueError("invalid value"),
... TypeError("invalid type"),
... KeyError("missing key"),
... ],
... )
... except* ValueError:
... print("Handling ValueError")
... except* TypeError:
... print("Handling TypeError")
... except* KeyError:
... print("Handling KeyError")
...
Handling ValueError
Handling TypeError
Handling KeyError
Mit der exclusive*
-Syntax können Sie einzelne Ausnahmen in einer Ausnahmegruppe abfangen. Auf diese Weise können Sie einzelne Ausnahmen gezielt behandeln.
Abschluss
Sie haben etwas über die integrierten Ausnahmen von Python erfahren, die eine schnelle und effiziente Möglichkeit bieten, Fehler und Ausnahmesituationen in Ihrem Code zu behandeln. Jetzt kennen Sie die am häufigsten verwendeten integrierten Ausnahmen, wissen, wann sie auftreten und wie Sie sie in Ihrem Ausnahmebehandlungscode verwenden. Sie haben außerdem erfahren, dass Sie diese Ausnahmen in Ihrem Code auslösen können.
In diesem Tutorial haben Sie:
- Erfahren Sie, was Fehler und Ausnahmen in Python sind
- Verstanden, wie Python die eingebauten Ausnahmen in einer Klassenhierarchie organisiert
- Untersucht die am häufigsten verwendeten integrierten Ausnahmen
- Erfahren Sie, wie Sie in Ihrem Code integrierte Ausnahmen behandeln und auslösen
Dieses Wissen hilft Ihnen, Ihren Code effizient zu debuggen, da jede Ausnahme eine bestimmte Bedeutung und einen bestimmten Anwendungsfall hat. Sie werden außerdem in der Lage sein, integrierte Ausnahmen in Ihrem Code effektiv zu behandeln und auszulösen, was für einen Python-Entwickler eine großartige Fähigkeit ist.