Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
1 Einleitung
2 Die Programmiersprache Python
Teil I Einstieg in Python
3 Erste Schritte im interaktiven Modus
4 Der Weg zum ersten Programm
5 Kontrollstrukturen
6 Dateien
7 Das Laufzeitmodell
8 Funktionen, Methoden und Attribute
9 Informationsquellen zu Python
Teil II Datentypen
10 Das Nichts – NoneType
11 Operatoren
12 Numerische Datentypen
13 Sequenzielle Datentypen
14 Zuordnungen
15 Mengen
16 Collections
17 Datum und Zeit
18 Aufzählungstypen – Enum
Teil III Fortgeschrittene Programmiertechniken
19 Funktionen
20 Modularisierung
21 Objektorientierung
22 Ausnahmebehandlung
23 Iteratoren
24 Kontextobjekte
25 Manipulation von Funktionen und Methoden
Teil IV Die Standardbibliothek
26 Mathematik
27 Kryptografie
28 Reguläre Ausdrücke
29 Schnittstelle zu Betriebssystem und Laufzeitumgebung
30 Kommandozeilenparameter
31 Dateisystem
32 Parallele Programmierung
33 Datenspeicherung
34 Netzwerkkommunikation
35 Debugging und Qualitätssicherung
36 Dokumentation
Teil V Weiterführende Themen
37 Anbindung an andere Programmiersprachen
38 Distribution von Python-Projekten
39 Grafische Benutzeroberflächen
40 Python als serverseitige Programmiersprache im WWW – ein Einstieg in Django
41 Wissenschaftliches Rechnen
42 Insiderwissen
43 Von Python 2 nach Python 3
A Anhang
Stichwortverzeichnis

Download:
- Beispielprogramme, ca. 464 KB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Python 3 von Johannes Ernesti, Peter Kaiser
Das umfassende Handbuch
Buch: Python 3

Python 3
Pfeil 22 Ausnahmebehandlung
Pfeil 22.1 Exceptions
Pfeil 22.1.1 Eingebaute Exceptions
Pfeil 22.1.2 Werfen einer Exception
Pfeil 22.1.3 Abfangen einer Exception
Pfeil 22.1.4 Eigene Exceptions
Pfeil 22.1.5 Erneutes Werfen einer Exception
Pfeil 22.1.6 Exception Chaining
Pfeil 22.2 Zusicherungen – assert
 
Zum Seitenanfang

22    Ausnahmebehandlung Zur vorigen ÜberschriftZur nächsten Überschrift

Stellen Sie sich einmal ein Programm vor, das über eine vergleichsweise tiefe Aufrufhierarchie verfügt, das heißt, dass Funktionen weitere Unterfunktionen aufrufen, die ihrerseits wieder Funktionen aufrufen. Es ist häufig so, dass die übergeordneten Funktionen nicht korrekt weiterarbeiten können, wenn in einer ihrer Unterfunktionen ein Fehler aufgetreten ist. Die Information, dass ein Fehler aufgetreten ist, muss also durch die Aufrufhierarchie nach oben geschleust werden, damit jede übergeordnete Funktion auf den Fehler reagieren und sich daran anpassen kann.

 
Zum Seitenanfang

22.1    Exceptions Zur vorigen ÜberschriftZur nächsten Überschrift

Bislang konnten wir Fehler, die innerhalb einer Funktion aufgetreten sind, allein anhand des Rückgabewertes der Funktion kenntlich machen. Es ist mit viel Aufwand verbunden, einen solchen Rückgabewert durch die Funktionshierarchie nach oben durchzureichen, zumal es sich dabei um Ausnahmen handelt. Wir würden also sehr viel Code dafür aufwenden, um seltene Fälle zu behandeln.

Für solche Fälle unterstützt Python ein Programmierkonzept, das Exception Handling (dt. »Ausnahmebehandlung«) genannt wird. Im Fehlerfall erzeugt unsere Unterfunktion dann eine sogenannte Exception und wirft sie, bildlich gesprochen, nach oben. Die Ausführung der Funktion ist damit beendet. Jede übergeordnete Funktion hat jetzt drei Möglichkeiten:

  • Sie fängt die Exception ab, führt den Code aus, der für den Fehlerfall vorgesehen ist, und fährt dann normal fort. In einem solchen Fall bemerken weitere übergeordnete Funktionen die Exception nicht.
  • Sie fängt die Exception ab, führt den Code aus, der für den Fehlerfall vorgesehen ist, und wirft die Exception weiter nach oben. In einem solchen Fall ist auch die Ausführung dieser Funktion sofort beendet, und die übergeordnete Funktion steht vor der Wahl, die Exception abzufangen oder nicht.
  • Sie lässt die Exception passieren, ohne sie abzufangen. In diesem Fall ist die Ausführung der Funktion sofort beendet, und die übergeordnete Funktion steht vor der Wahl, die Exception abzufangen oder nicht.

Bisher haben wir bei einer solchen Ausgabe

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

ganz allgemein von einem »Fehler« oder einer »Fehlermeldung« gesprochen. Dies ist nicht ganz korrekt: Im Folgenden möchten wir diese Ausgabe als Traceback bezeichnen. Welche Informationen ein Traceback enthält und wie sie interpretiert werden können, wurde bereits in Abschnitt 4.5, »Der Fehlerfall«, behandelt. Ein Traceback wird immer dann angezeigt, wenn eine Exception bis nach ganz oben durchgereicht wurde, ohne abgefangen zu werden. Doch was genau ist eine Exception?

Eine Exception ist ein Objekt, das Attribute und Methoden zur Klassifizierung und Bearbeitung eines Fehlers enthält. Einige dieser Informationen werden im Traceback angezeigt, so etwa die Beschreibung des Fehlers (»name 'abc' is not defined«). Eine Exception kann im Programm selbst abgefangen und behandelt werden, ohne dass der Benutzer etwas davon mitbekommt. Näheres zum Abfangen einer Exception erfahren Sie im weiteren Verlauf dieses Kapitels. Sollte eine Exception nicht abgefangen werden, wird sie in Form eines Tracebacks ausgegeben, und der Programmablauf wird beendet.

 
Zum Seitenanfang

22.1.1    Eingebaute Exceptions Zur vorigen ÜberschriftZur nächsten Überschrift

In Python existiert eine Reihe eingebauter Exceptions, zum Beispiel die bereits bekannten Exceptions SyntaxError, NameError oder TypeError. Solche Exceptions werden von Funktionen der Standardbibliothek oder vom Interpreter selbst geworfen. Sie sind eingebaut, das bedeutet, dass sie zu jeder Zeit im Quelltext verwendet werden können:

>>> NameError
<class 'NameError'>
>>> SyntaxError
<class 'SyntaxError'>

Die eingebauten Exceptions sind hierarchisch organisiert, das heißt, sie erben von gemeinsamen Basisklassen. Sie sind deswegen in ihrem Attribut- und Methodenumfang weitestgehend identisch.

Im Anhang in Abschnitt A.3 finden Sie eine Liste der eingebauten Exception-Typen mit kurzer Erklärung.

BaseException

Die Klasse BaseException ist die Basisklasse aller Exceptions und stellt damit eine Grundfunktionalität bereit, die für alle Exception-Typen vorhanden ist. Aus diesem Grund soll sie hier besprochen werden.

Die Grundfunktionalität, die BaseException bereitstellt, besteht aus einem wesentlichen Attribut namens args. Dabei handelt es sich um ein Tupel, in dem alle Parameter abgelegt werden, die der Exception bei ihrer Instanziierung übergeben wurden. Über diese Parameter ist es dann später beim Fangen der Exception möglich, detaillierte Informationen über den aufgetretenen Fehler zu erhalten. Die Verwendung des Attributs args demonstriert nun das folgende Beispiel:

>>> e = BaseException("Hallo Welt")
>>> e.args
('Hallo Welt',)
>>> e = BaseException("Hallo Welt",1,2,3,4,5)
>>> e.args
('Hallo Welt', 1, 2, 3, 4, 5)

Soweit zunächst zur direkten Verwendung der Exception-Klassen. Eine Erklärung aller eingebauten Exception-Klassen finden Sie im Anhang.

 
Zum Seitenanfang

22.1.2    Werfen einer Exception Zur vorigen ÜberschriftZur nächsten Überschrift

Bisher haben wir nur Exceptions betrachtet, die in einem Fehlerfall vom Python-Interpreter geworfen wurden. Es ist jedoch auch möglich, mithilfe der raise-Anweisung selbst eine Exception zu werfen:

>>> raise SyntaxError("Hallo Welt")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SyntaxError: Hallo Welt

Dazu wird das Schlüsselwort raise, gefolgt von einer Instanz, geschrieben. Diese darf nur Instanz einer von BaseException abgeleiteten Klasse sein. Das Werfen von Instanzen anderer Datentypen, insbesondere von Strings, ist nicht möglich:

>>> raise "Hallo Welt"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: exceptions must derive from BaseException

Im folgenden Abschnitt möchten wir besprechen, wie Exceptions im Programm abgefangen werden können, sodass sie nicht in einem Traceback enden, sondern zur Ausnahmebehandlung eingesetzt werden können. Wir werden sowohl in diesem als auch im nächsten Abschnitt bei den eingebauten Exceptions bleiben. Selbst definierte Exceptions werden das Thema von Abschnitt 22.1.4, »Eigene Exceptions«, sein.

 
Zum Seitenanfang

22.1.3    Abfangen einer Exception Zur vorigen ÜberschriftZur nächsten Überschrift

In diesem Abschnitt geht es darum, wie eine in einer Unterfunktion geworfene Exception in den darüberliegenden Aufrufebenen abgefangen werden kann. Das Fangen einer Exception ist notwendig, um auf den aufgetretenen Fehler reagieren zu können. Stellen Sie sich ein Programm vor, das Daten aus einer vom Benutzer festgelegten Datei liest. Dazu verwendet das Programm die folgende, im Moment noch sehr simple Funktion get, die das geöffnete Dateiobjekt zurückgibt:

def get(name):
return open(name)

Sollte keine Datei mit dem angegebenen Namen existieren, wirft die eingebaute Funktion open eine FileNotFoundError-Exception. Da die Funktion get nicht auf diese Exception reagiert, wird sie in der Aufrufhierarchie weiter nach oben gereicht und verursacht schließlich ein vorzeitiges Beenden des Programms.

Nun sind fehlerhafte Benutzereingaben Probleme, die Sie beim Schreiben eines interaktiven Programms berücksichtigen sollten. Die folgende Variante der Funktion get fängt eine von open geworfene FileNotFoundError-Exception ab und gibt in diesem Fall anstelle des geöffneten Dateiobjekts den Wert None zurück.

def get(name):
try:
return open(name)
except FileNotFoundError:
return None

Zum Abfangen einer Exception wird eine try/except-Anweisung verwendet. Eine solche Anweisung besteht zunächst aus zwei Teilen:

  • Der try-Block wird durch das Schlüsselwort try eingeleitet, gefolgt von einem Doppelpunkt und einem beliebigen Code-Block, der um eine Ebene weiter eingerückt ist. Dieser Code-Block wird zunächst ausgeführt. Wenn in diesem Code-Block eine Exception auftritt, wird seine Ausführung sofort beendet und der except-Zweig der Anweisung ausgeführt.
  • Der except-Zweig wird durch das Schlüsselwort except eingeleitet, gefolgt von einer optionalen Liste von Exception-Typen, für die dieser except-Zweig ausgeführt werden soll. Beachten Sie, dass mehrere Exception-Typen in Form eines Tupels angegeben werden müssen. Dazu werden Sie später noch ein Beispiel sehen. Hinter der Liste der Exception-Typen kann, ebenfalls optional, das Schlüsselwort as stehen, gefolgt von einem frei wählbaren Bezeichner. Hier legen Sie fest, unter welchem Namen Sie auf die gefangene Exception-Instanz im except-Zweig zugreifen können. Auf diesem Weg können Sie beispielsweise auf die in dem args-Attribut der Exception-Instanz abgelegten Informationen zugreifen. Auch dazu werden Sie im Verlauf dieses Kapitels noch Beispiele sehen.
    Danach folgen ein Doppelpunkt und, um eine Ebene weiter eingerückt, ein beliebiger Code-Block. Dieser Code-Block wird nur dann ausgeführt, wenn innerhalb des try-Blocks eine der aufgelisteten Exceptions geworfen wurde.

Eine grundlegende try/except-Anweisung hat also folgende Struktur:

Struktur einer try/except-Anweisung

Abbildung 22.1    Struktur einer try/except-Anweisung

Kommen wir zurück zu unserer Beispielfunktion get. Es ist durchaus möglich, dass bei einem Funktionsaufruf für name fälschlicherweise kein String, sondern zum Beispiel eine Liste übergeben wird. In einem solchen Fall wird kein FileNotFoundError, sondern ein TypeError geworfen, der von der try/except-Anweisung bislang nicht abgefangen wird:

>>> get([1,2,3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in get
TypeError: expected str, bytes or os.PathLike object, not list

Die Funktion soll nun dahingehend erweitert werden, dass auch ein TypeError abgefangen und dann ebenfalls None zurückgegeben wird. Dazu haben wir im Wesentlichen drei Möglichkeiten. Die erste besteht darin, die Liste der abzufangenden Exception-Typen im vorhandenen except-Zweig um den TypeError zu erweitern. Beachten Sie dabei, dass zwei oder mehr Exception-Typen im Kopf eines except-Zweiges als Tupel angegeben werden müssen.

def get(name):
try:
return open(name)
except (FileNotFoundError, TypeError):
return None

Dies ist einfach und führt im gewählten Beispiel zum gewünschten Resultat. Stellen Sie sich jedoch vor, Sie wollten je nach Exception-Typ unterschiedlichen Code ausführen. Um ein solches Verhalten zu erreichen, kann eine try/except-Anweisung über beliebig viele except-Zweige verfügen.

def get(name):
try:
return open(name)
except FileNotFoundError:
return None
except TypeError:
return None

Die dritte – weniger elegante – Möglichkeit besteht darin, alle Arten von Exceptions auf einmal abzufangen. Dazu wird ein except-Zweig ohne Angabe eines Exception-Typs geschrieben:

def get(name):
try:
return open(name)
except:
return None
[»]  Hinweis

Es ist nur in wenigen Fällen sinnvoll, alle möglichen Exceptions auf einmal abzufangen. Durch diese Art Exception Handling kann es vorkommen, dass unabsichtlich auch Exceptions abgefangen werden, die nichts mit dem oben dargestellten Code zu tun haben. Das betrifft zum Beispiel die KeyInterrupt-Exception, die bei einem Programmabbruch per Tastenkombination geworfen wird.

Sollen Sie einmal jede beliebige Exception fangen wollen, verwenden Sie except Exception, da Exception die Basisklasse alle Exceptions ist, die das Programm nicht zwingend beenden.

Eine Exception ist nichts anderes als eine Instanz einer bestimmten Klasse. Darum stellt sich die Frage, ob und wie man innerhalb eines except-Zweiges Zugriff auf die geworfene Instanz erlangt. Das ist durch Angabe des bereits angesprochenen as Bezeichner-Teils im Kopf des except-Zweiges möglich. Unter dem dort angegebenen Namen können Sie nun innerhalb des Code-Blocks auf die geworfene Exception-Instanz zugreifen:[ 102 ](Die möglicherweise verwirrende Schreibweise print([1,2,3][10]) ist gleichbedeutend mit lst = [1,2,3]
print(lst[10]))

try:
print([1,2,3][10])
except (IndexError, TypeError) as e:
print("Fehlermeldung:", e.args[0])

Die Ausgabe des oben angeführten Beispiels lautet:

Fehlermeldung: list index out of range

Zusätzlich kann eine try/except-Anweisung über einen else- und einen finally-Zweig verfügen, die jeweils nur einmal pro Anweisung vorkommen dürfen. Der dem else-Zweig zugehörige Code-Block wird ausgeführt, wenn keine Exception aufgetreten ist, und der dem finally-Zweig zugehörige Code-Block wird in jedem Fall nach Behandlung aller Exceptions und nach dem Ausführen des entsprechenden else-Zweiges ausgeführt, egal, ob oder welche Exceptions vorher aufgetreten sind. Dieser finally-Zweig eignet sich daher besonders für Dinge, die in jedem Fall erledigt werden müssen, wie beispielsweise das Schließen eines Dateiobjekts.

Sowohl der else- als auch der finally-Zweig müssen ans Ende der try/except-Anweisung geschrieben werden. Wenn beide Zweige vorkommen, muss der else-Zweig vor dem finally-Zweig stehen.

Abbildung 22.2 zeigt eine vollständige try/except-Anweisung.

Eine vollständige try/except-Anweisung

Abbildung 22.2    Eine vollständige try/except-Anweisung

Abschließend noch einige Bemerkungen dazu, wie eine try/except-Anweisung ausgeführt wird: Zunächst wird der dem try-Zweig zugehörige Code ausgeführt. Sollte innerhalb dieses Codes eine Exception geworfen werden, wird der dem entsprechenden except-Zweig zugehörige Code ausgeführt. Ist kein passender except-Zweig vorhanden, wird die Exception nicht abgefangen und endet, wenn sie auch anderswo nicht abgefangen wird, als Traceback auf dem Bildschirm. Sollte im try-Zweig keine Exception geworfen werden, wird keiner der except-Zweige ausgeführt, sondern der else-Zweig. Der finally-Zweig wird in jedem Fall zum Schluss ausgeführt.

Exceptions, die innerhalb eines except-, else- oder finally-Zweiges geworfen werden, werden so behandelt, als würfe die gesamte try/except-Anweisung diese Exception. Exceptions, die in diesen Zweigen geworfen werden, können also nicht von folgenden except-Zweigen der gleichen Anweisung wieder abgefangen werden. Es ist jedoch möglich, try/except-Anweisungen zu verschachteln:

try:
try:
raise TypeError
except IndexError:
print("Ein IndexError ist aufgetreten")
except TypeError:
print("Ein TypeError ist aufgetreten")

Im try-Zweig der inneren try/except-Anweisung wird ein TypeError geworfen, der von der Anweisung selbst nicht abgefangen wird. Die Exception wandert dann, bildlich gesprochen, eine Ebene höher und durchläuft die nächste try/except-Anweisung. In dieser wird der geworfene TypeError abgefangen und eine entsprechende Meldung ausgegeben. Die Ausgabe des Beispiels lautet also: Ein TypeError ist aufgetreten, es wird kein Traceback angezeigt.

 
Zum Seitenanfang

22.1.4    Eigene Exceptions Zur vorigen ÜberschriftZur nächsten Überschrift

Beim Werfen und Abfangen von Exceptions sind Sie nicht auf den eingebauten Satz von Exception-Typen beschränkt, vielmehr können Sie selbst neue Typen erstellen. Dazu brauchen Sie lediglich eine eigene Klasse zu erstellen, die von der Exception-Basisklasse Exception erbt, und dann ganz nach Anforderung weitere Attribute und Methoden zum Umgang mit Ihrer Exception hinzuzufügen.

Im Folgenden definieren wir zunächst eine rudimentäre Kontoklasse, die als einzige Operation das Abheben eines bestimmten Geldbetrags unterstützt.

class Konto:
def __init__(self, betrag):
self.kontostand = betrag
def abheben(self, betrag):
self.kontostand -= betrag

In dieser Implementierung der Klasse ist es möglich, das Konto beliebig zu überziehen. In einer etwas raffinierteren Variante soll das Überziehen des Kontos unterbunden und beim Versuch, mehr Geld abzuheben, als vorhanden ist, eine selbst definierte Exception geworfen werden. Dazu definieren wir zunächst eine von der Basisklasse Exception abgeleitete Klasse und fügen Attribute für den Kontostand und den abzuhebenden Betrag hinzu.

class KontoException(Exception):
def __init__(self, kontostand, betrag):
self.kontostand = kontostand
self.betrag = betrag

Dann modifizieren wir die Methode abheben der Klasse Konto dahingehend, dass bei einem ungültigen Abhebevorgang eine KontoException-Instanz geworfen wird.

class Konto:
def __init__(self, betrag):
self.kontostand = betrag
def abheben(self, betrag):
if betrag > self.kontostand:
raise KontoException(self.kontostand, betrag)
self.kontostand -= betrag

Die dem Konstruktor der Klasse übergebenen zusätzlichen Informationen werden im Traceback nicht angezeigt:

>>> k = Konto(1000)
>>> k.abheben(2000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in abheben
KontoException: (1000, 2000)

Sie kommen erst zum Tragen, wenn die Exception abgefangen und bearbeitet wird:

try:
k.abheben(2000)
except KontoException as e:
print("Kontostand: {}€".format(e.kontostand))
print("Abheben von {}€ nicht möglich.".format(e.betrag))

Dieser Code fängt die entstandene Exception ab und gibt daraufhin eine Fehlermeldung aus. Anhand der zusätzlichen Informationen, die die Klasse durch die Attribute kontostand und betrag bereitstellt, lässt sich der vorausgegangene Abhebevorgang rekonstruieren. Die Ausgabe des Beispiels lautet:

Kontostand: 1000€
Abheben von 2000€ nicht möglich.

Damit eine selbst definierte Exception mit weiterführenden Informationen auch eine Fehlermeldung enthalten kann, muss sie die Magic Method __str__ implementieren:

class KontoException(Exception):
def __init__(self, kontostand, betrag):
self.kontostand = kontostand
self.betrag = betrag
def __str__(self):
return "Kontostand zu niedrig"

Ein Traceback, der durch diese Exception verursacht wird, sieht folgendermaßen aus:

>>> k = Konto(1000)
>>> k.abheben(2000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in abheben
KontoException: Kontostand zu niedrig
 
Zum Seitenanfang

22.1.5    Erneutes Werfen einer Exception Zur vorigen ÜberschriftZur nächsten Überschrift

In manchen Fällen, gerade bei einer tiefen Funktionshierarchie, ist es sinnvoll, eine Exception abzufangen, die für diesen Fall vorgesehene Fehlerbehandlung zu starten und die Exception danach erneut zu werfen. Dazu folgendes Beispiel:

def funktion3():
raise TypeError
def funktion2():
funktion3()
def funktion1():
funktion2()
funktion1()

Im Beispiel wird die Funktion funktion1 aufgerufen, die ihrerseits funktion2 aufruft, in der die Funktion funktion3 aufgerufen wird. Es handelt sich also um insgesamt drei verschachtelte Funktionsaufrufe. Im Innersten dieser Funktionsaufrufe, in funktion3, wird eine TypeError-Exception geworfen. Diese Exception wird nicht abgefangen, deshalb sieht der dazugehörige Traceback so aus:

Traceback (most recent call last):
File "test.py", line 10, in <module>
funktion1()
File "test.py", line 8, in funktion1
return funktion2()
File "test.py", line 5, in funktion2
return funktion3()
File "test.py", line 2, in funktion3
raise TypeError
TypeError

Der Traceback beschreibt erwartungsgemäß die Funktionshierarchie zum Zeitpunkt der raise-Anweisung. Diese Liste wird auch Callstack genannt.

Hinter dem Exception-Prinzip steht der Gedanke, dass sich eine Exception in der Aufrufhierarchie nach oben arbeitet und an jeder Station abgefangen werden kann. In unserem Beispiel soll die Funktion funktion1 die TypeError-Exception abfangen, damit sie eine spezielle, auf den TypeError zugeschnittene Fehlerbehandlung durchführen kann. Nachdem funktion1 ihre funktionsinterne Fehlerbehandlung durchgeführt hat, soll die Exception weiter nach oben gereicht werden. Dazu wird sie erneut geworfen wie im folgenden Beispiel:

def funktion3():
raise TypeError
def funktion2():
funktion3()
def funktion1():
try:
funktion2()
except TypeError:
# Fehlerbehandlung
raise TypeError
funktion1()

Im Gegensatz zum vorangegangenen Beispiel sieht der nun auftretende Traceback so aus:

Traceback (most recent call last):
File "test.py", line 14, in <module>
funktion1()
File "test.py", line 12, in funktion1
raise TypeError
TypeError

Sie sehen, dass dieser Traceback Informationen über den Kontext der zweiten raise-Anweisung enthält. Diese sind aber gar nicht von Belang, sondern eher ein Nebenprodukt der Fehlerbehandlung innerhalb der Funktion funktion1. Optimal wäre es, wenn trotz des temporären Abfangens der Exception in funktion1 der resultierende Traceback den Kontext der ursprünglichen raise-Anweisung beschriebe. Um das zu erreichen, wird eine raise-Anweisung ohne Angabe eines Exception-Typs geschrieben:

def funktion3():
raise TypeError
def funktion2():
funktion3()
def funktion1():
try:
funktion2()
except TypeError as e:
# Fehlerbehandlung
raise
funktion1()

Der in diesem Beispiel ausgegebene Traceback sieht folgendermaßen aus:

Traceback (most recent call last):
File "test.py", line 16, in <module>
funktion1()
File "test.py", line 11, in funktion1
funktion2()
File "test.py", line 7, in funktion2
funktion3()
File "test.py", line 4, in funktion3
raise TypeError
TypeError

Sie sehen, dass es sich dabei um den Stacktrace der Stelle handelt, an der die Exception ursprünglich geworfen wurde. Der Traceback enthält damit die gewünschten Informationen über die Stelle, an der der Fehler tatsächlich aufgetreten ist.

 
Zum Seitenanfang

22.1.6    Exception Chaining Zur vorigen ÜberschriftZur nächsten Überschrift

Gelegentlich kommt es vor, dass man innerhalb eines except-Zweiges in die Verlegenheit kommt, eine weitere Exception zu werfen – entweder weil bei der Behandlung der Exception ein weiterer Fehler aufgetreten ist, oder um die entstandene Exception »umzubenennen«.

Wenn innerhalb eines except-Zweiges eine weitere Exception geworfen wird, wendet Python automatisch das sogenannte Exception Chaining an. Dabei wird die vorangegangene Exception als Kontext an die neu geworfene Exception angehängt, sodass ein Maximum an Information weitergegeben wird. Zum Beispiel erzeugt der folgende Code:

try:
[1,2,3][128]
except IndexError:
raise RuntimeError("Schlimmer Fehler")

die Ausgabe:

Traceback (most recent call last):
File "test.py", line 3, in <module>
[1,2,3][128]
IndexError: list index out of range
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "test.py", line 5, in <module>
raise RuntimeError("Schlimmer Fehler") from e
RuntimeError: Schlimmer Fehler

Es wird auf das 128. Element einer dreielementigen Liste zugegriffen, was eine IndexError-Exception provoziert. Diese Exception wird gefangen und bei der Behandlung eine RuntimeError-Exception geworfen. Anhand des ausgegebenen Tracebacks sehen Sie, dass die ursprüngliche IndexError-Exception an die neue RuntimeError-Exception angehängt wurde.

Mithilfe der raise/from-Syntax lässt sich das Exception-Chaining-Verhalten steuern. Beim Werfen einer Exception kann ein Kontext angegeben werden, der dann im resultierenden Traceback berücksichtigt wird. Dieser Kontext kann zum Beispiel eine zweite Exception sein:

>>> raise IndexError from ValueError
ValueError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError

Es zeigt sich, dass wir mit der raise/from-Syntax das Exception Chaining auslösen können. Alternativ kann mit der raise/from-Syntax das automatische Anhängen einer Exception verhindert werden:

try:
[1,2,3][128]
except IndexError:
raise RuntimeError("Schlimmer Fehler") from None

In diesem Fall enthält der resultierende Traceback lediglich die neu entstandene RuntimeError-Exception. Die ursprüngliche IndexError-Exception geht verloren.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Python 3 Python 3
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Einstieg in Python
Einstieg in Python


Zum Rheinwerk-Shop: Python. Der Grundkurs
Python. Der Grundkurs


Zum Rheinwerk-Shop: Algorithmen mit Python
Algorithmen mit Python


Zum Rheinwerk-Shop: Objektorientierte Programmierung
Objektorientierte Programmierung


Zum Rheinwerk-Shop: Raspberry Pi. Das umfassende Handbuch
Raspberry Pi. Das umfassende Handbuch


Zum Rheinwerk-Shop: Roboter-Autos mit dem Raspberry Pi
Roboter-Autos mit dem Raspberry Pi


Zum Rheinwerk-Shop: Neuronale Netze programmieren mit Python
Neuronale Netze programmieren mit Python


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2020
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern