Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einleitung
2 Überblick über Python
3 Die Arbeit mit Python
4 Der interaktive Modus
5 Grundlegendes zu Python-Programmen
6 Kontrollstrukturen
7 Das Laufzeitmodell
8 Basisdatentypen
9 Benutzerinteraktion und Dateizugriff
10 Funktionen
11 Modularisierung
12 Objektorientierung
13 Weitere Spracheigenschaften
14 Mathematik
15 Strings
16 Datum und Zeit
17 Schnittstelle zum Betriebssystem
18 Parallele Programmierung
19 Datenspeicherung
20 Netzwerkkommunikation
21 Debugging
22 Distribution von Python-Projekten
23 Optimierung
24 Grafische Benutzeroberflächen
25 Python als serverseitige Programmiersprache im WWW mit Django
26 Anbindung an andere Programmiersprachen
27 Insiderwissen
28 Zukunft von Python
A Anhang
Stichwort

Download:
- ZIP, ca. 4,8 MB
Buch bestellen
Ihre Meinung?

Spacer
 <<   zurück
Python von Peter Kaiser, Johannes Ernesti
Das umfassende Handbuch - Aktuell zu Python 2.5
Buch: Python

Python
gebunden, mit CD
819 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1110-9
Pfeil 13 Weitere Spracheigenschaften
  Pfeil 13.1 Exception Handling
    Pfeil 13.1.1 Eingebaute Exceptions
    Pfeil 13.1.2 Werfen einer Exception
    Pfeil 13.1.3 Abfangen einer Exception
    Pfeil 13.1.4 Eigene Exceptions
    Pfeil 13.1.5 Erneutes Werfen einer Exception
  Pfeil 13.2 List Comprehensions
  Pfeil 13.3 Docstrings
  Pfeil 13.4 Generatoren
  Pfeil 13.5 Iteratoren
  Pfeil 13.6 Interpreter im Interpreter
  Pfeil 13.7 Geplante Sprachelemente
  Pfeil 13.8 Die with-Anweisung
  Pfeil 13.9 Function Decorator
  Pfeil 13.10 assert
  Pfeil 13.11 Weitere Aspekte der Syntax
    Pfeil 13.11.1 Umbrechen langer Zeilen
    Pfeil 13.11.2 Zusammenfügen mehrerer Zeilen
    Pfeil 13.11.3 String conversions

»Die Grenzen meiner Sprache sind die Grenzen meiner Welt« – Ludwig Wittgenstein

13 Weitere Spracheigenschaften

Zu diesem Zeitpunkt sollten Sie bereits relativ gut in Python programmieren können. In diesem Kapitel werden wir einige weitere Spracheigenschaften von Python behandeln. Wichtig ist, dass dieses Kapitel kein Sammelbecken für »den uninteressanten Rest« darstellt, sondern dass viele der hier vorgestellten Techniken sehr elegant und wichtig sind. Betrachten Sie dieses Kapitel also als essentielle Ergänzung zum bisher Gelernten.


Galileo Computing - Zum Seitenanfang

13.1 Exception Handling  Zur nächsten ÜberschriftZur vorigen Ü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. Es ist also notwendig, die Information, dass ein Fehler aufgetreten ist, durch die Aufrufhierarchie nach oben zu schleusen, damit jede übergeordnete Funktion auf den Fehler reagieren und sich daran anpassen kann.

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

Für genau solche Fälle unterstützt Python ein Programmierkonzept, das Exceptionhandling (dt. Ausnahmebehandlung) genannt wird. Im Fehlerfall würde unsere Unterfunktion dann eine sogenannte Exception erzeugen und, bildlich gesprochen, nach oben werfen. 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 diese verwendet werden können, wurde bereits in Abschnitt 5.4, »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 eine Klasse, die Attribute und Methoden zur Klassifizierung und Bearbeitung des 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, so wird sie in Form eines Tracebacks ausgegeben und der Programmablauf wird beendet.


Galileo Computing - Zum Seitenanfang

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

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

>>> NameError 
<type 'exceptions.NameError'> 
>>> SyntaxError 
<type 'exceptions.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. Die Vererbungshierarchie sieht so aus wie in Abbildung 13.1.

Abbildung 13.1  Vererbungshierarchie der eingebauten Exceptions

Nachfolgend soll die Verwendung der Exception BaseException besprochen werden, da sie die Basisklasse aller Exceptions darstellt. Sie stellt eine gewisse Grundfunktionalität bereit, die folglich für alle Exception-Typen vorhanden ist. Im Anhang finden Sie eine Beschreibung aller eingebauten Exceptions.

BaseException

Die Klasse BaseException verfügt im Wesentlichen über zwei Attribute namens message und args, in denen nähere Informationen zur Exception stehen können. Das Tupel args enthält alle bei der Instanziierung von BaseException übergebenen Argumente. Wenn nur eines übergeben wurde, wird dieses zusätzlich in message geschrieben, sodass direkt auf dieses Argument zugegriffen werden kann:

>>> e = BaseException("Hallo Welt") 
>>> e.message 
'Hallo Welt' 
>>> e.args 
('Hallo Welt',)

Werden bei der Instanziierung mehrere Argumente übergeben, so werden diese in das Tupel args geschrieben. Das Attribut message ist in diesem Fall ein leerer String:

>>> e = BaseException(1,2,3,4,5,6) 
>>> e.message 
'' 
>>> e.args 
(1, 2, 3, 4, 5, 6)

Die Klasse BaseException erlaubt es, durch Slicing auf die gespeicherten Argumente zuzugreifen:

>>> e = BaseException(1,2,3,4,5,6) 
>>> e[2:4] 
(3, 4)

Galileo Computing - Zum Seitenanfang

13.1.2 Werfen einer Exception  Zur nächsten ÜberschriftZur vorigen Ü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 selbst erstellten Klasse oder eines vordefinierten Exception-Typs sein. Es ist zwar auch möglich, einen String zu werfen, davon wird jedoch ausdrücklich abgeraten, da dies als »deprecated« eingestuft wurde und in späteren Python-Versionen nicht mehr möglich sein wird:

>>> raise "Hallo Welt" 
__main__:1: DeprecationWarning: raising a string exception is deprecated 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
Hallo Welt

Das Werfen anderer Datentypen ist nicht möglich:

>>> raise 123 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
TypeError: exceptions must be classes, instances, or strings  
(deprecated), not int

Die raise-Anweisung kann in zwei Varianten verwendet werden. Die erste Möglichkeit haben Sie eben kennengelernt, also das Schlüsselwort raise, gefolgt von einer Instanz einer Klasse.

Durch die zweite Verwendungsmöglichkeit von raise spart man sich die explizite Instanziierung der Klasse. Dazu folgen auf das Schlüsselwort raise drei durch Kommata getrennte Angaben:

  • Die erste Angabe muss der Datentyp der Exception sein, die geworfen werden soll. Da die beiden folgenden Angaben optional sind, kann eine raise-Anweisung auch so aussehen:
>>> raise SyntaxError 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
SyntaxError: None
    • In diesem Fall wird automatisch eine Instanz der Klasse SyntaxError erzeugt und geworfen. Der Konstruktor der Klasse bekommt dabei keine Argumente übergeben, sodass hier beispielsweise keine Fehlermeldung erscheint (SyntaxError: None).
  • Als zweite, optionale Angabe der raise-Anweisung kann ein Argument für den Konstruktor der Klasse übergeben werden. Im Falle einer Standard-Exception entspricht dieses der Fehlermeldung.
>>> raise SyntaxError, "Dies ist ein Fehler" 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
SyntaxError: Dies ist ein Fehler
>>> raise SyntaxError, [1,2,3,4] 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
SyntaxError: [1, 2, 3, 4]
  • Die dritte, ebenfalls optionale Angabe der raise-Anweisung kann ein sogenanntes Traceback-Objekt sein. Ein solches Objekt hält den aktuellen Programmstatus fest und erlaubt es somit, eine Exception aus einem bestimmten Kontext heraus zu werfen, obwohl sich der Programmfluss selbst nicht in diesem Kontext befindet. Näheres zum Traceback-Objekt erfahren Sie im Zusammenhang mit dem Modul traceback in Abschnitt 21.6.
  • 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. Beachten Sie, dass wir sowohl in diesem als auch im nächsten Kapitel bei den eingebauten Exceptions bleiben. Selbstdefinierte Exceptions werden das Thema von Abschnitt 13.1.4 sein.

Galileo Computing - Zum Seitenanfang

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

Es wurde bereits gesagt, dass eine Exception innerhalb des Programms abgefangen und behandelt werden kann. Stellen Sie sich dazu einmal vor, wir wollten eine Funktion schreiben, die es uns erlaubt, auf ein Element einer Liste lst mit dem Index n zuzugreifen. Die Funktion muss intern prüfen, ob ein n-tes Element in lst existiert, und, wenn ja, dieses zurückgeben. Sollte kein solches Element existieren, soll die Funktion None zurückgeben.

Nach Ihrem bisherigem Kenntnisstand würde die Funktion folgendermaßen aussehen:

def get(lst, n): 
    if 0 <= n < len(lst): 
        return lst[n] 
    else: 
        return None

Eine zweite – und in vielen Fällen elegantere – Variante wäre es, einfach auf das n-te Element der Liste lst zuzugreifen und eine eventuell geworfene IndexError-Exception abzufangen. Wenn diese Exception abgefangen wurde, gibt die Funktion None zurück:

def get(lst, n): 
    try: 
        return lst[n] 
    except IndexError: 
        return None

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

  • Der try-Block wird durch das Schlüsselwort try eingeleitet, gefolgt von einem Doppelpunkt und, um eine Ebene weiter eingerückt, einem beliebigen Codeblock. Dieser Codeblock wird zunächst ausgeführt. Wenn in diesem Codeblock 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, einem Doppelpunkt und, um eine Ebene weiter eingerückt, einem beliebigen Codeblock. Dieser Codeblock 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:

Abbildung 13.2  Struktur einer try/except-Anweisung

Kommen wir zurück zu unserer Beispielfunktion get. Es wäre durchaus möglich, dass bei einem Funktionsaufruf für n fälschlicherweise keine ganze Zahl, sondern zum Beispiel ein String übergeben wird. In einem solchen Fall wird kein Index Error, sondern ein TypeError geworfen, welcher von der try/except-Anweisung bislang nicht abgefangen wird:

>>> get([1,2,3], "s") 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
  File "<stdin>", line 3, in get 
TypeError: list indices must be integers

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 wäre es, 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.

try: 
    return lst[n] 
except (IndexError, TypeError): 
    return None

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

try: 
    return lst[n] 
except IndexError: 
    return None 
except TypeError: 
    return None

Die dritte – weniger elegante – Möglichkeit wäre es, alle Exceptions auf einmal abzufangen. Dazu wird einfach ein except-Zweig ohne Angabe eines Exception-Typs geschrieben:

try: 
    return lst[n] 
except: 
    return None

Hinweis
Beachten Sie unbedingt, dass es nur in wenigen Fällen sinnvoll ist, alle möglichen Exceptions auf einmal abzufangen. Durch diese Art Exception Handlings kann es vorkommen, dass unabsichtlich auch Exceptions abgefangen werden, die nichts mit dem obigen Code zu tun haben. Das betrifft unter anderem die KeyInterrupt-Exception, die bei einem Programmabbruch per Tastenkombination geworfen wird.


Eine Exception ist nichts anderes als eine Instanz einer Klasse. Von daher stellt sich die Frage, ob und wie man innerhalb eines except-Zweiges Zugriff auf die geworfene Instanz erlangt. Das ist möglich, indem nach dem Tupel der abzufangenden Exception-Typen und durch ein Komma von diesem getrennt ein Bezeichner geschrieben wird. Unter diesem Namen kann nun innerhalb des Codeblocks auf die geworfene Exception zugegriffen werden. Dies könnte folgendermaßen aussehen:

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

Die Ausgabe des obigen 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 ein einziges Mal pro Anweisung vorkommen dürfen. Der dem else-Zweig zugehörige Codeblock wird ausgeführt, wenn keine Exception aufgetreten ist, und der dem finally-Zweig zugehörige Codeblock wird in jedem Fall nach Behandlung aller Exceptions und nach dem Ausführen des else-Zweigs 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.

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

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

Abbildung 13.3  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, so wird der dem entsprechenden except-Zweig zugehörige Code ausgeführt. Ist kein passender except-Zweig vorhanden, so 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, so wird keiner der except-Zweige ausgeführt, sondern zunächst der else- und dann der finally-Zweig, wobei beide Zweige optional sind. Beachten Sie, dass der finally-Zweig in jedem Fall, also auch wenn Exceptions aufgetreten sind, zum Schluss ausgeführt wird.

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 nachfolgenden 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.


Galileo Computing - Zum Seitenanfang

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

Beim Werfen und Abfangen von Exceptions sind Sie nicht auf den eingebauten Satz von Exception-Typen beschränkt, vielmehr können Sie selbst beliebige neue Typen erstellen. Theoretisch brauchen Sie dabei auch keine besonderen Regeln einhalten, denn eine Exception kann eine beliebige selbst definierte Klasse sein.

Im Programmieralltag werden Sie jedoch vermutlich kaum eine selbst definierte Exception von Grund auf neu schreiben, vielmehr möchten Sie auf das Verhalten der vorhandenen Exception-Typen zurückgreifen und nur Platz für weitere Informationen schaffen oder gar überhaupt keine Änderungen vornehmen.

In solchen Fällen erstellen Sie eine eigene Klasse, die von einer der eingebauten Exceptions erbt. Dazu bietet sich die Basisklasse Exception an. Die folgende Beispielfunktion soll zwei ganze Zahlen dividieren und im Falle einer Division durch Null keinen ZeroDivisionError, sondern einen eigenen Exception-Typ mit weiteren Informationen werfen. Dazu definieren wir zunächst eine von Exception abgeleitete Klasse und fügen ein Attribut für den Zähler der Division hinzu:

class DivisionByZeroError(Exception): 
    def __init__(self, z): 
        self.zaehler = z

Dann definieren wir die Funktion. Sie erwartet zwei Parameter und gibt das Ergebnis ihrer Division zurück. Wenn der Nenner null ist, wird die soeben erstellte Klasse DivisionByZeroError geworfen:

def division(z, n): 
    if n == 0: 
        raise DivisionByZeroError(z) 
    return z / n

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

Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
  File "<stdin>", line 4, in division 
__main__.DivisionByZeroError

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

try: 
    division(12, 0) 
except DivisionByZeroError, e: 
    print "Nulldivision: %d / 0" % e.zaehler

Dieser Code fängt die entstandene Exception ab und gibt daraufhin eine Fehlermeldung aus. Aufgrund der zusätzlichen Informationen, die die Klasse durch das Attribut zaehler bereitstellt, lässt sich die vorangegangene Berechnung rekonstruieren. Die Ausgabe des Beispiels lautet:

Nulldivision: 12 / 0

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

class DivisionByZeroError(Exception): 
    def __init__(self, z): 
        self.zaehler = z 
 
    def __str__(self): 
        return "Division durch Null"

Ein Traceback, der durch diese Exception verursacht wird, würde folgendermaßen aussehen:

>>> raise DivisionByZeroError(12, 0) 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
__main__.DivisionByZeroError: Division durch Null

Das Exception Handling hilft ungemein beim Schreiben von strukturiertem und lesbarem Code, sodass Sie diese Techniken verinnerlichen sollten. Wir werden auch im Laufe dieses Buches immer wieder Exceptions verwenden.


Galileo Computing - Zum Seitenanfang

13.1.5 Erneutes Werfen einer Exception  topZur vorigen Überschrift

In vielen 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 Call Stack genannt.

Der Gedanke, der hinter dem Exception-Prinzip steht, ist der, 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. So könnte dann beispielsweise ein Dateiobjekt geschlossen werden. 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 vorherigen 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

Man sieht, 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 beschreiben würde. 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, 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.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






 <<   zurück
  
  Zum Katalog
Zum Katalog: Python






Python
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Linux






 Linux


Zum Katalog: Ubuntu GNU/Linux






 Ubuntu GNU/Linux


Zum Katalog: Praxisbuch Web 2.0






 Praxisbuch Web 2.0


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Praxisbuch Objektorientierung






 Praxisbuch Objektorientierung


Zum Katalog: Einstieg in SQL






 Einstieg in SQL


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für Fachinformatiker


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2008
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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de