Kontrollfluss
Bedingungen
- die allgemeine Form der bedingten Auswahlanweisung besitzt folgende Form:
if expression:
statements
elif expression:
statements
elif expression:
statements
...
else:
statements
- die Angabe der elif beziehungsweise else Anweisung ist optional
- Python kennt keine case Anweisung, denn sie stellen für objektorientierte Sprachen keinen guten Stildar
- mittels Dictionaries und anonymen Funktionen sind case Strukturen schnell simuliert:
select={
"+": (lambda x,y: x+y),
"-": (lambda x,y: x-y),
"*": (lambda x,y: x*y),
"/": (lambda x,y: x/y) }
result= select["+"](3,4)
- übersetze die if Anweisungen in eine Dictionary Abfrage; else Anweisung
def choiceIf(choice):
if choice == 1:
val= "red"
elif choice == 2:
val= "green"
elif choice == 3:
val= "blue"
else:
val="white"
return val
- da leere Anweisungen syntaktisch falsch sind, muß hier explizit das Schlüsselwort pass verwendet werden
if expression:
pass
else:
statements
- dies gilt auch für Funktionen und Methoden
Schleifen
- beide Schleifentypen - while and for - können mit einer optional else Anweisung formuliert werden
- der else Programmblock wird genau dann ausgeführt, wenn die Schleifeniteration vollständig ausgeführt wurde
while
- die while Schleife besitzt folgende Form
while expression:
statements
else:
statements
- Python erlaubt keine Zuweisungen an Stellen, in denen Ausdrücke erwartet werden
- folgender gültige C Ausdruck ist daher nicht zulässig
- nextLine soll die nächste Zeile liefern
while( line= nextLine() ){ process(line); }
- Das entsprechende Python-Idiom lautet:
for line in nextLine(): process(line)
for
- die for Schleife besitzt folgende Form, wobei i über die Sequenz s iteriert
for i in s:
statements
else:
statements
- mittels break und continuekann der Schleifendurchlauf manipuliert werden
- continue beendet die aktuelle Schleife und fährt gegebenfalls mit dem nächsten Schleifendurchlauf weiter
- break beendet die while bzw. for Schleife
- Python kennt keine Schleife, bei der am Ende des Schleifendurchlaufs die Bedingung geprüft wird
- Iteriere über folgende Sequenzen und gibt sie mittels print aus
- range(-10,10)
- ["red","green","blue"]
- "TestString"
- [(1,"eins"),(2,"zwei"),(3,"drei"),(4,"vier")]
- gib die Tupels
- gib den ersten Wert des Tupels aus
- {1:"eins",2:"zwei",3:"drei",4:"vier"}
- gib die Items
- gib jeweils nur die keys und values aus
- open("/etc/passwd") und
urllib.urlopen("http://www.python.org")
Ausnahmen - Exceptions
Integraler Bestandteil
- bei syntaktische Fehlern wirft der Pythoninterpreter eine Ausnahme
- ein paar verschiedene Ausnahmen
Sind die Ausnahmemeldungen intuitiv?
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero
>>> d={]
File "<stdin>", line 1
d={]
^
SyntaxError: invalid syntax
>>> d={}
>>> d["gibtsNicht"]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
KeyError: 'gibtsNicht'
>>> arr=[]
>>> arr[1]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
IndexError: list index out of range
>>> import Bibilothek
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ImportError: No module named Bibilothek
>>> 3+"string"
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: unsupported operand type(s) for +: 'int' and 'str'
- Python besitzt eine differenzierte built-in Ausnahmenhierachie:
- fängt man die Ausnahme durch einen Ausnahmebehandlung nicht ab, so passiert folgendes:
- die Ausnahme wird bis zum Mainprogramm weiterpropagiert
- im Mainprogramm wird der default exception handler aufgerufen
- der default exception handler schreibt eine Fehlermeldung, die den Typ der Ausnahme und den stack trace (die Liste der aktiven Zeilen und Funktionen, als die Ausnahme geworfen wurde) enthält
- der default exception handler beendet (terminates) das Programm
- Exceptions brechen aus dem normalen Kontrollfluß aus
Aspekte der Ausnahmebehandlung
fangen mit try/except/else
Beispiele
- eine Ausnahme zu fangen in seiner einfachsten Form:
>>> try:
... 7/0
... except:
... print "Kein Fehler!"
...
Kein Fehler!
- es geht auch spezieller:
>>> try:
... 7/0
... except ZeroDivisionError:
... print "Kein Fehler"
...
Kein Fehler
- mit kleinen Überraschungen
>>> try:
... 7/0
... except StandardError:
... print "Zu allgemein"
... except ZeroDivisionError:
... print "Toter Code"
...
Zu allgemein
- die except Handler werden sukzessive abgearbeitet
- der erste exception handler, der zutrifft wird benützt
- first match for best match
- die exception handler sollten vom speziellen zum allgemeinen definiert werden
- optional kann noch ein abschließendes else angegeben werden, das genau dann prozessiert wird, wenn keine Ausnahme auftrat
>>> try:
... 7/1
... except:
... pass
... else:
... print "keine Ausnahme"
...
7
keine Ausnahme
- falls die Ausnahme nicht gefangen werden konnte, führt dies in der Regel zum Programmabruch
>>> try:
... 7/0
... except IndexError:
... pass
...
Traceback (most recent call last):
File "<stdin>", line 2, in ?
ZeroDivisionError: integer division or modulo by zero
- mittels reguläre expressions kann geprüft werden, ob ein String einer gültigen Zahl entspricht
def is_a_number(x):
import re
num_re = re.compile(r'^[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][-+]?[0-9]+)?$')
matchObject = num_re.match(str(x))
if matchObject:
return True
else:
return False
- dies geht deutlich einfacher mit Exceptions
schreibe eine Funktion mit der gleichen Funktionalität
Syntax
- die Ausnahmebehandlung kann leicht komplex werden
try:
<Anweisungen> # wird zuerst ausgeführt
except <name1>:
<Anweisungen> # wird ausgeführt, falls eine Ausnahme vom Type name1 geworfen wurde
except <name2>,<Daten>:
<Anweisungen> # wird ausgeführt, falls Ausnahme name2 geworfen wurde, erhalte dabei zusätzlich Daten
except (name3,name4):
<Anweisungen> # wird ausgeführt, falls eine Ausnahme vom Typ name3 oder name3 geworfen wurde
except:
<Anweisungen> # fange die restlichen Ausnahmen auf, und führe die Anweisungen aus
else:
<Anweisungen> # wird ausgeführt, falls der try Block keine Ausnahme warf
- Annahme:
- in try werden jeweils Ausnahmen vom Typ name1, name2, eine nicht aufgezählt Ausnahme und keine Ausnahme geworfen
- dann kommt es zu folgendem Kontrollfluß nach dem try Block:
- name1:
- try except <name1>
- name2:
- try except <name1> except <name2>, <Daten>
- nicht aufgezähler Typ:
- try except <name1> except <name2>, <Daten> except (name3,name4) except
- keine Exception:
- try else
- name1:
- Exceptions verhalten sich Sprunganweisungen (goto) deutlich ähnlicher als Funktionsaufrufen,
>>> try:
... 7/0
... print "Ignoriere die Zeile"
... except:
... print "Ignoriere den Fehler"
...
Ignoriere den Fehler
denn ein Funktionsaufruf würde nach gefangener Exception
print "Ignoriere die Zeile"
prozessieren
aufräumen mit try/finally
- häufig kommt es vor, daß im try Block eine Ressource (Datei,Socket,Lock,...) gebunden wird, die natürlich wieder freigegeben werden sollte
daß hätte aber zu Folge, daß in jedem exception handler und im abschließenden else Block die Ressource wieder freigeben werden müsste ( vgl.Syntaxbeispiel )- ein mühsames und fehlerträchtiges Unterfangen - aus diesem Grund bietet Python ein weiteres Konzept an, um mit Ausnahmebehandlungen umzugehen:
try:
lockARessource()
finally:
releaseTheRessource()
- unabhängig davon, ob eine Exception auftrat, wird der finally Block abgearbeitet
- durch die try Anweisung wird der Kontrollfluß immer zum finally Block gelenkt
- die beiden Konzepte try/except/else und try/finally können einschließlich Python 2.4 nur exclusive verwendet werden
- falls eine Ausnahme auftrat, wird diese nach dem Prozessieren des finally Block wieder geworfen
>>> def lockARessource(): raise IndexError
...
>>> try:
... try:
... lockARessource()
... finally:
... print "Räume auf"
... except IndexError:
... print "Error"
...
Räume auf
Error
werfen mit raise/assert
- bisher war wir nur in der passiven Situation: eine Exception trat auf und wir reagierten mit except/else oder finally darauf
- mittels raise ist es möglich, eine Exception explizit zu werfen
- folgende Variationen stehen zur Verfügung
raise <name> # wirf explizt eine Exception
raise <name>,<data> # übergibt zusätzlich Daten
raise # wirf die aktuelle Exception weiter
Beispiele
- wirf eine Exception
>>> def raiseError(): raise IndexError
>>> try:
... raiseError()
... except IndexError:
... print "Mein Index Error"
...
Mein Index Error
- Datenübergabe und Exceptions weiterwerfen ( rethrow )
def raiseError() : raise IndexError,"Ich werde wieder geworfen"
>>> try:
... raiseError()
... except IndexError,data:
... print data
... raise
...
Ich werde wieder geworfen
Traceback (most recent call last):
File "<stdin>", line 2, in ?
File "<stdin>", line 1, in raiseError
IndexError: Ich werde wieder geworfen
assert
- eine Sonderform unter den Exceptions nimmt die assertAnweisung in doppelter Hinsicht ein
- ihr werfen ist an eine Bedingung geküpft
- sie wird explizit durch das Aufrufen des Python Interpreters mit Optimierung -O" unterbunden
- die Syntax des Aufrufes ist
assert <test>,<data>
wobei dies äquivalent zu folgendem Ausdruck ist
if __debug__:
if not <test>:
raise AssertionError, <data>
- durch das explizite Ausschalten der Exceptions eignen sich die Assertion für die Entwicklung des Codes, während die restlichen Exceptions die Fehler im Produktivcode fokusieren
Weiterlesen...