Verzweigungen#
Programme können als Abfolge von durchnummerierten Statements betrachtet werden. Diese werden sequenziell (nacheinander) Statement nach Statement ausgeführt, in der Reihenfolge wie es im Programm definiert wurde. Werden Funktionen aufgerufen, so wird der in der Funktion definierte code an der Stelle des Aufrufs aufgeführt. Das folgende Beispiel soll dies verdeutlichen. Es definiert ein einfaches Skript was den Pythagoras zweier Variablen x
und y
mit Hilfe einer Funktion berechnet. Um den Programablauf zu verdeutlichen wurde jede Zeile (bis auf Zeile 4, da dies im Syntax der Funktionsdefinition nicht funktioniert) mit einem Printstatement der Zeile erweitert.
import math
x = 3; print("Zeile 1")
y = 4; print("Zeile 2")
print("Zeile 3")
def pythagoras(a, b):
res = math.sqrt((a**2)+(b**2)); print("Zeile 5")
return res; print("Zeile 6");
print("Zeile 7")
p_xy = pythagoras(x, y); print("Zeile 8")
p_yx = pythagoras(y, x); print("Zeile 9")
print("Zeile 10")
Zeile 1
Zeile 2
Zeile 3
Zeile 7
Zeile 5
Zeile 8
Zeile 5
Zeile 9
Zeile 10
Nach Ausführung des Programms erkennen wir das die Zeilen 1 bis 3 in sequentieller Reihenfolge ausgeführt werden. Dann überspringt die Ausführung die Zeilen 5 und 6, weil diese die Funktion pythagoras
definiert, diese aber bisher noch nicht aufgerufen worden ist. Die Funktion wird erst am Anfang von Zeile 8 aufgerufen, weshalb als nächstes die Zeile 5 vor Zeile 8 ausgeführt wird. Es fehlt dann die Ausgabe der ‘Zeile 6’, weil die Funktion bereits mit dem return
beendet worden ist. Da die Funktion nochmals mit vertauschten Parametern in Zeile 9 aufgerufen wird, wird wieder Zeile 5 ausgegeben und dann erst Zeile 9 und 10.
Damit sehen wir sowohl, dass das Programm sequenziell von oben nach unten ausgeführt worden ist. Da die Funktionen allerdings wiederholbaren Statements kanzeln, werden diese bei Aufruf mehrmals ausgeführt.
Verzweigungen#
if
die Wenn-Dann Verzweigung#
Oft ist es Notwendig in Programmen auch Entscheidungen zu treffen und bestimmte Statements nur in bestimmten Fällen auszuführen. Hierfür bieten alle Programmiersprachen unterschiedliche Möglichkeiten den Programmcode zu Verzweigen.
Die Einfachste Form der Verzweigung sind bedingte Verzweigungen, welche einen Code-Block nur bei Zutreffen einer logischen Bedingung ausführen. In Python ist dafür die if
-Anweisung (Wenn-Dann) vorgesehen, welche einem Statement vorsteht, welches nur bei Zutreffen einer Bedingung ausgeführt wird. Es ist üblich die Statements auf eine neue Zeile zu schreiben. Dann müssen sie eingerückt werden.
bedingung=True
if bedingung:
# wird nur ausgeführt wenn Bedingung True ist
print(f"Der Block mit 'bedingung={bedingung}' wurde ausgeführt.")
bedingung=False
if bedingung:
# wird nur ausgeführt wenn Bedingung True ist
print(f"Der Block mit 'bedingung={bedingung}' wurde ausgeführt.")
Der Block mit 'bedingung=True' wurde ausgeführt.
Mit dem logischen Operator not
kann man die Bedingungen negieren und mit and
, or
und Klammern kombinieren.
bedingung=True
if not bedingung:
# wird nur ausgeführt wenn Bedingung True ist
print(f"Der Block mit 'bedingung={bedingung}' wurde ausgeführt.")
bedingung=0
if bedingung:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
bedingung=1
if bedingung:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
if not bedingung:
# wird nur ausgeführt wenn Bedingung True ist
print(f"Der Block mit 'bedingung={bedingung}' wurde ausgeführt.")
Der Block mit 'bedingung'=1 wurde ausgeführt.
Strings die leer und die Länge 0 haben entsprechen dem logischen Wert False
und nicht leere dem logischen Wert True
.
bedingung='' # leerer String
if bedingung:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
bedingung='nicht_leer'
if bedingung:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
Der Block mit 'bedingung'=nicht_leer wurde ausgeführt.
Das gleiche gilt für Listen und Dictionaries, wenn diese leer sind (False
) oder befüllt (True
).
bedingung=[] # leerer Liste
if bedingung:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
bedingung=['nicht_leer'] # nicht leere Liste
if bedingung:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
Der Block mit 'bedingung'=['nicht_leer'] wurde ausgeführt.
Damit lässt sich im Code schnell prüfen ob z.B. bereits Elemente in einer Liste enthalten sind und diese z.B. weiterverarbeitet werden sollen.
Bei komplexeren Datentypen, wie Objekten, die den Wert None
annehmen können, wird dieser auch als False
interpretiert und alles andere als True
, womit man wiederum prüfen kann, ob die Variable schon belegt worden ist.
bedingung=None # nicht belegt
if bedingung:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
bedingung="nicht_None" # belegt
if bedingung:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
bedingung=None # nicht belegt
if bedingung is not None:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
bedingung="nicht_None" # belegt
if bedingung is not None:
# wird nur ausgeführt wenn Bedingung equivalent mit True ist
print(f"Der Block mit 'bedingung'={bedingung} wurde ausgeführt.")
Der Block mit 'bedingung'=nicht_None wurde ausgeführt.
Der Block mit 'bedingung'=nicht_None wurde ausgeführt.
else
- Die Alternative#
Oft kommt es vor, dass nicht nur etwas ausgeführt werden soll, wenn die Bedingung wahr ist, sondern im anderen Fall auch etwas ausgeführt werden soll. Dafür gibt es die else
-Anweisung, welche ausschließlich nach einem if
folgen darf und bei mehreren Zeilen auf gleicher Höhe eingerückt werden muss, wie das if
welches es beschließt.
bedingung=True
if bedingung:
# wird nur ausgeführt wenn Bedingung True ist
print(f"Der Block mit 'bedingung={bedingung}' wurde ausgeführt.")
else:
# wird nur ausgeführt wenn Bedingung False ist
print(f"Der alternative Block mit 'bedingung={bedingung}' wurde ausgeführt.")
Der Block mit 'bedingung=True' wurde ausgeführt.
bedingung=False
if bedingung:
# wird nur ausgeführt wenn Bedingung True ist
print(f"Der Block mit 'bedingung={bedingung}' wurde ausgeführt.")
else:
# wird nur ausgeführt wenn Bedingung False ist
print(f"Der alternative Block mit 'bedingung={bedingung}' wurde ausgeführt.")
Der alternative Block mit 'bedingung=False' wurde ausgeführt.
Ein typisches Beispiel ist das Abfangen von Divisionen durch 0. Da man bei Berechnungen nicht immer garantieren kann, dass der Nenner 0 sein kann. Da diese nicht definiert ist, führt sie zu einem Programmfehler und ggf. zu einem Programmabsturz.
zaehler = 5
nenner = 0
ergebnis = zaehler / nenner
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In[9], line 3
1 zaehler = 5
2 nenner = 0
----> 3 ergebnis = zaehler / nenner
ZeroDivisionError: division by zero
Um dies abzufangen können wir eine if-else
-Verzweigung nutzen und dem Ergebnis den Wert None
(im Sinne von ‘nicht definiert’) zuweisen.
nenner = 0
if nenner: # der Wahrheitswert eines int-Wertes ist False bei 0 und Wahr für != 0
ergebnis = zaehler / nenner
else:
print("Teilung durch 0")
ergebnis = None
print(f"Das Ergebnis von {zaehler}/{nenner} = {ergebnis}")
Teilung durch 0
Das Ergebnis von 5/0 = None
Da man ferner bei Python durch die dynamische Typisierung gar nicht garantieren kann, dass zaehler
und nenner
vom richtigen Datentyp int
sind, ist es sinnvoll eine Typprüfung hinzuzufügen. Hierfür prüfen wir zuerst den Datentyp mit der bekannten Funktion type()
bevor wir die Prüfung auf 0-Teilung machen. Dafür verschachteln wir die if-else
Bedingungen.
zaehler = 'keine_zahl'
nenner = 0
if type(zaehler) == int:
if type(nenner) == int:
if nenner: # der Wahrheitswert eines int-Wertes ist False bei 0 und Wahr für != 0
ergebnis = zaehler / nenner
else:
print("Teilung durch 0")
ergebnis = None
else:
print(f"Nenner nicht vom Datentyp `int`, sondern vom Typ {type(nenner)}")
ergebnis = None
else:
print(f"Zaehler nicht vom Datentyp `int`, sondern vom Typ {type(zaehler)}")
ergebnis = None
print(f"Das Ergebnis von {zaehler}/{nenner} = {ergebnis}")
Zaehler nicht vom Datentyp `int`, sondern vom Typ <class 'str'>
Das Ergebnis von keine_zahl/0 = None
elif
- Für noch mehr Alternativen#
Gibt es wie im letzten Beispiel mehrere verschachtelte Alternativen, so wird der Code sehr schnell unübersichtlich und man braucht eine kompaktere Schreibweise. Hierfür gibt es in Python die elif
-Anweisung, welche einfach nur eine Kurzform von else if
ist und keines weiteren Einrückens bedarf. So, kann man den vorherigen Code leserlicher schreiben als:
zaehler = 'keine_zahl'
nenner = 0
ergebnis = None
if not isinstance(nenner, int):
print(f"Nenner nicht vom Datentyp `int`, sondern vom Typ {type(nenner)}")
elif not isinstance(zaehler, int):
print(f"Zaehler nicht vom Datentyp `int`, sondern vom Typ {type(zaehler)}")
elif not nenner:
print("Teilung durch 0")
else:
ergebnis = zaehler / nenner
print(f"Das Ergebnis von {zaehler}/{nenner} = {ergebnis}")
Zaehler nicht vom Datentyp `int`, sondern vom Typ <class 'str'>
Das Ergebnis von keine_zahl/0 = None
Hierbei haben wir:
als erstes die Bedingung
type(zaehler) == int
ersetzt durch die Standardfunktionisinstance(zaehler, int)
welche identisch ist und einfacher zu lesen.wir haben alle Fehlerfälle zuerst bearbeitet, um sicher zu stellen, dass sie vor Ausführung der Berechnung abgefragt werden
so prüfen wir erst ob der
zaehler
nicht vom Datentypint
ist und geben in diesem Fall eine Fehlermeldung ausIst das nicht der Fall, so könnte
nenner
nicht vom Datentypint
sein und würden in diesem Fall eine andere Fehlermeldung ausgebendann muss muss der
nenner
vom Datentypint
sein und wir prüfen ob er 0 ist mit einer entsprechenden Fehlermeldungda in allen drei Fällen das
ergebnis
den WertNone
hat, haben wir diese Zuweisung vor denif
-Block gesetztder letzte
else
Block wird nur erreicht, wenn all dies nicht zutrifft, alsozaehler
undnenner
vom Datentypenint
undnenner
nicht 0 ist. Nur in diesem Fall wirdergebnis
mit dem Wert der Teilung belegt
Note
Hierbei ist zu beachten, dass wir absichtlich die Fehlermeldung “Teilung durch 0” nach hinten bewegt haben. Es wäre auch möglich mit der Bedingung if not nenner
in der originalen Reihenfolge zu starten. Hier gäbe es allerdings die Gefahr, dass Aufgrund der Wahrheitswerte anderer Datentypen auch z.B. eine leere Liste diese Bedingung korrekt erfüllen wurde. Wir bekommen in diesem Fall eine falsche Fehlermeldung angezeigt (“Teilung durch 0” statt “Nenner vom Datentyp int
”), wie folgendes Beispiel zeigt. Das ist eine der typischen Gefahren von dynamisch typisierten Sprachen. Grundsätzlich sind deshalb Datentyp-Prüfungen immer zuerst zu machen.
zaehler = 10
nenner = []
ergebnis = None
if not nenner:
print("Teilung durch 0")
elif not isinstance(nenner, int):
print(f"Nenner nicht vom Datentyp `int`, sondern vom Typ {type(nenner)}")
elif not isinstance(zaehler, int):
print(f"Zaehler nicht vom Datentyp `int`, sondern vom Typ {type(zaehler)}")
else:
ergebnis = zaehler / nenner
print(f"Das Ergebnis von {zaehler}/{nenner} = {ergebnis}")
Teilung durch 0
Das Ergebnis von 10/[] = None
match
- Bei vielen Alternativen einer Variable#
In vielen Programmiersprachen gibt es zum Behandeln vieler Alternativen einer einzelnen Variable die match
-Anweisung. Sie gibt es erst seit der Version 3.10 in Python. Damit kann ein solcher Fall etwas kompakter als mit elif
-Anweisungen behandelt werden.
variable = "c"
match(variable):
case "a": # if variable == "a"
print("Wert war 'a'")
case "b": # elif variable == "b"
print("Wert war 'b'")
case "c": # elif variable == "c"
print("Wert war 'c'")
case _: # else
print("Wert war nicht 'a', 'b' oder 'c'")
Wert war 'c'