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 Standardfunktion isinstance(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 Datentyp int ist und geben in diesem Fall eine Fehlermeldung aus

  • Ist das nicht der Fall, so könnte nenner nicht vom Datentyp int sein und würden in diesem Fall eine andere Fehlermeldung ausgeben

  • dann muss muss der nenner vom Datentyp int sein und wir prüfen ob er 0 ist mit einer entsprechenden Fehlermeldung

  • da in allen drei Fällen das ergebnis den Wert None hat, haben wir diese Zuweisung vor den if-Block gesetzt

  • der letzte else Block wird nur erreicht, wenn all dies nicht zutrifft, also zaehler und nenner vom Datentypen int und nenner nicht 0 ist. Nur in diesem Fall wird ergebnis 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'