Verzweigungen#

Midjourney: Red or Blue Pill, ref. Leonardo da Vinci

When you come to a fork in the road, take it.

— Yogi Berra

Folien/PDF#

Bedingtes Verzweigen#

Ein Computer muss oft entscheiden wie Informationen verarbeitet werden sollen. Dafür muss er im Programm die Möglichkeit haben, bestimmte Statements oder Programmteile nur in bestimmten Fällen auszuführen. Hierfür bieten alle Programmiersprachen das Konzept der Verzweigung an.

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. 1")

bedingung=False
if bedingung:
    # wird nur ausgeführt wenn Bedingung True ist
    print(f"Der Block mit 'bedingung={bedingung}' wurde ausgeführt. 2")
Der Block mit 'bedingung=True' wurde ausgeführt. 1

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=2
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'=2 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=' '
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'=  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.

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[8], 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

Alternatives Verzweigen#

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

ℹ️ Info: 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

Viele Alternativen#

In Python 3.10 wurde das Match-Statement eingeführt, was es in vielen anderen Sprachen bereits gibt. Es erlaub eine Variable mit mehren möglichen Werten zu vergleichen.

Traditionell (viele elif):

if Variable == ValueA:
    StatementA
elif Variable == ValueB:
    StatementB
else:
    StatementC

Neu (match-case):

match Variable:
    case ValueA:
        StatementA
    case ValueB:
        StatementB
    case _:
        StatementC

Quiz#

--- shuffleQuestions: true shuffleAnswers: true --- ### Was macht eine `if`-Anweisung in Python? - [x] Führt einen Block aus, wenn die Bedingung wahr ist - [ ] Wiederholt Code mehrfach - [ ] Beendet das Programm - [ ] Fragt den Nutzer nach einer Eingabe ### Welche Syntax ist korrekt für eine `if`-Bedingung in Python? - [x] if x > 5: - [ ] if (x > 5) - [ ] if x > 5 then - [ ] if x > 5 {} ### Was passiert bei der Bedingung `if not x:` in Python? - [x] Der Codeblock wird ausgeführt, wenn `x` als False interpretiert wird - [ ] Der Codeblock wird bei `x == True` ausgeführt - [ ] Die Variable `x` wird gelöscht - [ ] Python erzeugt einen Fehler ### Welche dieser Werte gelten in Python als `False`? - [x] Leere Liste `[]` - [x] Der Wert `None` - [x] Leerer String `""` - [ ] Die Zahl `1` ### Wann wird ein `else`-Block in Python ausgeführt? - [x] Wenn die `if`-Bedingung nicht zutrifft - [ ] Wenn die `if`-Bedingung wahr ist - [ ] Immer, wenn ein Fehler auftritt - [ ] Direkt nach dem Start des Programms ### Wofür wird `else` häufig verwendet? - [x] Um einen alternativen Code auszuführen - [ ] Um Funktionen zu definieren - [ ] Um Variablen zu löschen - [ ] Um Einrückungen zu prüfen ### Was ist `elif` in Python? - [x] Kurzform für „else if“ - [ ] Eine Methode zur String-Verarbeitung - [ ] Ein logischer Operator - [ ] Ein alternativer Schleifentyp ### Warum ist `elif` nützlich? - [x] Es macht den Code kürzer und übersichtlicher - [ ] Es ersetzt die `while`-Schleife - [ ] Es deaktiviert `else` - [ ] Es zwingt Python zum Neustart ### Was macht `isinstance(x, int)`? - [x] Prüft, ob `x` vom Typ `int` ist - [ ] Wandelt `x` in einen `int` um - [ ] Löscht die Variable `x` - [ ] Erzeugt eine neue Instanz ### Warum ist Typprüfung in Python nützlich? - [x] Um Fehler zu vermeiden, z. B. bei Division durch 0 - [ ] Weil Python stark typisiert ist - [ ] Um Variablen in Funktionen zu kopieren - [ ] Um Schleifen zu beenden ### Sortiere die folgenden Zeilen, um ein korrekt eingerücktes `if-else`-Statement zu erhalten. Geben ist: ```python a = 3 b = 5 ``` 1. `if a < b:` 2. ` print("a ist kleiner als b")` 3. `else:` 4. ` print("a ist größer oder gleich b")` ### Sortiere die Zeilen korrekt für eine Funktion mit `if` und `return`: 1. ` if x > 0:` 2. ` return "positiv"` 3. ` else:` 4. ` return "nicht positiv"` ### Bringe die `if-elif-else`-Struktur in korrekte Reihenfolge: 1. `if note == 1:` 2. ` print("sehr gut")` 3. `elif note >= 4:` 4. ` print("bestanden")` 5. `else:` 6. ` print("nicht bestanden")` ### Welcher Fehler ist in folgendem Code enthalten? ```python if x > 5 print("x ist größer als 5") ``` - [x] Der Doppelpunkt `:` nach der Bedingung fehlt - [ ] Die Einrückung ist zu groß - [ ] `if` darf nicht mit `>` kombiniert werden - [ ] `print` darf nicht in einer `if`-Anweisung stehen ### Was stimmt bei diesem Code nicht? ```python if y == 10: print("y ist gleich 10") ``` - [x] Die Zeile mit `print` ist nicht eingerückt - [ ] Der Vergleichsoperator `==` ist ungültig - [ ] Die `if`-Bedingung darf kein `:` enthalten - [ ] Strings müssen in Hochkommas stehen ### Wo ist der Fehler in diesem Code? ```python if x > 5: return True else return False ``` - [x] Nach `else` fehlt ein Doppelpunkt `:` - [ ] `return` darf nicht in `if` stehen - [ ] Funktionen dürfen kein `if` enthalten - [ ] Die Klammern bei `test` sind falsch ### Welcher Fehler steckt in diesem Beispiel? ```python if x = 5: print("x ist 5") ``` - [x] Es wird `=` (Zuweisung) statt `==` (Vergleich) verwendet - [ ] `x` ist kein gültiger Variablenname - [ ] Der `print`-Befehl ist veraltet - [ ] Zahlen dürfen nicht verglichen werden