Kontrollfluss#

(brit flag control-flow)

In den Programmen, die wir bisher gesehen haben, wurde immer eine Reihe von Anweisungen von Python exakt in der Reihenfolge von oben nach unten ausgeführt. Was wäre, wenn Sie den Ablauf ändern möchten? Wenn Sie z.B. möchten, daß das Programm Entscheidungen trifft und je nach Situation verschiedene Dinge tut, wie z.B. “Guten Morgen” oder “Guten Abend” abhängig von der Tageszeit ausgibt?

Wie Sie vielleicht bereits vermuten, wird dies durch Kontrollfluss-Anweisungen erreicht. In Python gibt es vier Kontrollfluss-Anweisungen: if, for und while. Seit Python Version 3.10 gibt es zusätzlich noch das match statement.


Die if-Anweisung#

  • (brit flag if: wenn)

  • (brit flag else: ansonsten)

  • (brit flag elif (else if): anderenfalls wenn)

Die if-Anweisung wird verwendet, um eine Bedingung zu prüfen: Wenn die Bedingung wahr ist, wird ein Block von Anweisungen ausgeführt (der sogenannte if-Block). Andernfalls wird ein anderer Block von Anweisungen verarbeitet (der sogenannte else-Block). Die else-Klausel ist optional.

Pro if-Anweisung darf es maximal eine else-Anweisung geben, aber beliebig viele elif-Anweisungen. elif steht für else if.

Beispiel if_de.py

Quellcode
 1geheim_zahl = 23
 2antwort_zahl = int(input('Errate meine Zahl (Ganzzahl): '))
 3
 4if antwort_zahl == geheim_zahl:
 5    # Anfang vom Code-Block
 6    print('Gratulation, richtig erraten!')
 7    print("Leider gibt's nichts zu gewinnen...")
 8    # Ende vom Code-Block
 9elif antwort_zahl < geheim_zahl:
10    # noch ein Code-Block
11    print('zu niedrig')
12    # Im Code-Block können beliebig viele Python-Zeilen stehen...
13else:
14    print('zu hoch')
15    # Dieser Block wird ausgeführt wenn die antwort_zahl
16    # größer ist als die geheim_zahl
17
18print('Fertig')
19# Diese Zeile wird immer ausgeführt 
20

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
$ python3 if_de.py
Enter an integer : 50
No, it is a little lower than that
Done

$ python3 if_de.py
Enter an integer : 22
No, it is a little higher than that
Done

$ python3 if_de.py
Enter an integer : 23
Congratulations, you guessed it.
(but you do not win any prizes!)
Done

Wie es funktioniert

In diesem Programm nehmen wir Vermutungen vom Benutzer entgegen und überprüfen, ob es sich um die von uns festgelegte Zahl handelt. Wir setzen die Variable number auf eine beliebige Ganzzahl, z. B. 23. Dann nehmen wir die Vermutung des Benutzers mit der Funktion input() entgegen. Funktionen sind wiederverwendbare Programmteile. Wir werden im nächsten Kapitel mehr darüber lesen.

Wir übergeben der eingebauten Funktion input eine Zeichenkette, die auf dem Bildschirm angezeigt wird und auf die Eingabe des Benutzers wartet. Sobald wir etwas eingeben und die Eingabetaste drücken, gibt die Funktion input() das Eingabefeld als Zeichenkette zurück. Anschließend wandeln wir diese Zeichenkette mit int in eine Ganzzahl um und speichern sie in der Variable guess. Tatsächlich ist int eine Klasse, aber alles, was Sie jetzt wissen müssen, ist, dass Sie sie verwenden können, um eine Zeichenkette in eine Ganzzahl umzuwandeln (vorausgesetzt, die Zeichenkette enthält eine gültige Ganzzahl im Text).

Als Nächstes vergleichen wir die Vermutung des Benutzers mit unserer Zahl. Wenn die Vermutung mit unserer Zahl übereinstimmt, geben wir aus, dass die Vermutung richtig war. Andernfalls geben wir aus, dass die Vermutung falsch war.


Die while-Schleife#

  • (brit flag while: während, währenddessen, solange wie)

  • (brit flag break: ausbrechen, entwischen)

Die while-Schleife ermöglicht es Ihnen, einen Block von Anweisungen solange auszuführen, wie eine Bedingung wahr ist.

Beispiel while_de.py

Quellcode
 1geheimzahl = 23
 2spielen = True
 3
 4while spielen:
 5    rate_zahl = int(input('Errate meine Geheimzahl : '))
 6
 7    if rate_zahl == geheimzahl:
 8        print('Gratulation, das war richtig!')
 9        spielen = False # die while Schleife wird nicht nochmal wiederholt
10    elif rate_zahl < geheimzahl:
11        print('zu klein')
12    else:
13        print('zu groß')
14else:
15    print('Die while Schleife ist fertig')
16    # Hier kann beliebiger Code stehen
17
18print('Fertig')

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
$ python3 while_de.py 
Errate meine Geheimzahl : 55
zu groß
Errate meine Geheimzahl : 11
zu klein
Errate meine Geheimzahl : 23
Gratulation, das war richtig!
Die while Schleife ist fertig
Fertig

Wie es funktionert

In diesem Programm nehmen wir wiederholt die Eingabe des Benutzers entgegen und geben die Länge jeder Eingabe aus. Wir bieten eine spezielle Bedingung an, um das Programm zu beenden, indem wir überprüfen, ob die Benutzereingabe 'quit' ist. Wir beenden das Programm, indem wir die Schleife mit break verlassen und das Ende des Programms erreichen.

Die Länge der Eingabezeichenkette kann mit der eingebauten Funktion len ermittelt werden.

Denken Sie daran, dass die Anweisung break auch mit der for-Schleife verwendet werden kann.


Swaroops poetisches Python#

Die von mir hier verwendete Eingabe ist ein Mini-Gedicht, das ich geschrieben habe:

Programmieren macht Spaß
wenn die Arbeit getan ist.
Wenn Du Spaß an der Arbeit haben willst:
    verwende Python!

Die continue-Anweisung#

  • (brit flag continue: weitermachen, fortsetzten)

Die continue-Anweisung wird verwendet, um Python mitzuteilen, dass es den Rest der Anweisungen im aktuellen Schleifenblock überspringen und mit der nächsten Iteration der Schleife fortfahren soll.

Beispiel [continue_de.py](programs/

continue_de.py)

class:

note

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
$ python3 continue_de.py
Schreib etwas : ja
Zu wenig
Schreib etwas : na
Zu wenig
Schreib etwas : jo
Zu wenig
Schreib etwas : echt?
Der Text ist lang genug
Schreib etwas : na gut
Der Text ist lang genug
Schreib etwas : aufhören

Wie es funktioniert

In diesem Programm nehmen wir Eingaben vom Benutzer entgegen, verarbeiten die Eingabezeichenkette jedoch nur, wenn sie mindestens 3 Zeichen lang ist. Daher verwenden wir die eingebaute Funktion len, um die Länge zu ermitteln. Wenn die Länge weniger als 3 beträgt, überspringen wir den Rest der Anweisungen im Block mit der Anweisung continue. Andernfalls werden die restlichen Anweisungen in der Schleife ausgeführt, um die gewünschte Verarbeitung durchzuführen.

Beachten Sie, dass die Anweisung continue auch mit der for-Schleife funktioniert.


Die match ... case Anweisung#

  • (brit flag match: zusammenpassen, übereinstimmen)

  • (brit flag case: Fall)

Added in version 3.10: Die match Anweisung wurde in Python ab Version 3.10 eingeführt

Ähnlich zu einem if ... elif ... else Block unterstüzt Python auch eine match Anweisung, die in manchen Fällen eleganteren Code erlaubt als die if Anweisungen.

Prinzipiell erwartet match einen Ausdruck und dieser Ausdruck wird dann mit mehreren case Blöcken verglichen. Wenn der Vergleich True ergibt, wird der Code im entsprechenden case-Block ausgeführt alle anderen case-Blöcke werden ignoriert (ähnlich wie beim elif). Die Besonderheit ist daß ein Pipe Symbol | als “oder” dient. Außerdem kann der Ausdruck in jedem Case Block in eine Variable (oder mehrere Variablen, bei einem Tuple / einer Liste) umgewandelt werden. Diese Variablen bleiben bestehen auch wenn der match Block beendet ist.

Man kann jeden case Befehl mit einen “Wächter” (guard) kombinieren: eine if-Anweisung welche prüft ob der case Block überhaupt ausgeführt werden darf. Und schlussendlich kann mit dem *-Präfix eine beliebige Anzahl von Variablen zusammengefasst werden (siehe auch *args und **kwargs in einem späteren Kapitel.)

Ein spezieller Fall ist der case _: Befehl: Er kann als abschließender case-Block eingebaut werden und ist das Äquivalent zum else Befehl.

Schauen Sie sich folgendes Beispiel an:

Beispiel match1_de.py

Quellcode
 1import random
 2geheimzahl = random.randint(1,10)
 3wörter = ["eins","zwei","drei","vier","fünf",
 4          "sechs","sieben","acht","neun","zehn"]
 5print("Zahlenraten")
 6while True:
 7    print("errate meine Zahl (1-10)")
 8    eingabe = input("(tippe `q` oder `ende` zum aufhören) >>>")
 9    match eingabe:
10        case "q" | "ende":
11            print("Spiel beendet")
12            break
13        case x if x.isdigit():
14            zahl = int(x)            
15            if not (0 < zahl < 11):
16                print("Bitte nur Zahlen zwischen 1 und 10 eingeben")
17                continue
18        case wort if wort in wörter:
19            zahl = wörter.index(wort) + 1 # index beginnt mit Null
20        case _:
21            print("ungültige Eingabe")
22            continue
23    # --- ende vom match .. case block
24    if zahl == geheimzahl:
25        print("Bravo richtig erraten! ich denke mir eine neue Zahl aus")
26        geheimzahl = random.randint(1,10)
27    elif zahl > geheimzahl:
28        print("falsch geraten, zu hoch! probier es nochmal")
29    else:
30        print("falsch geraten, zu niedrig! probier es nochmal")
31    
32

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
$ python3 match1_de.py 
Zahlenraten
errate meine Zahl (1-10)
(tippe `q` oder `ende` zum aufhören) >>>x
ungültige Eingabe
errate meine Zahl (1-10)
(tippe `q` oder `ende` zum aufhören) >>>5
falsch geraten, zu hoch! probier es nochmal
errate meine Zahl (1-10)
(tippe `q` oder `ende` zum aufhören) >>>drei
falsch geraten, zu hoch! probier es nochmal
errate meine Zahl (1-10)
(tippe `q` oder `ende` zum aufhören) >>>1
Bravo richtig erraten! ich denke mir eine neue Zahl aus
errate meine Zahl (1-10)
(tippe `q` oder `ende` zum aufhören) >>>q
Spiel beendet

Wie es funktioniert

Diese (leicht verbesserte) Funktion des Zahlenratenspiels akzeptiert sowohl Ziffern (3, 5 etc. ) als Eingabe als auch ausgeschreibene Wörter wie z.B. eins, zwei usw.

Der Anfang bis inkl. Zeile 8 ist nicht wesentlich anders als das Beispiel zu if weiter oben. Anstatt die Variable eingabe mit if...elif...else auszuwerten wird in Zeile 9 hier ein match ... case Block begonnen. In Zeile 10 (dem ersten case-Block) wird geprüft ob der Wert von eingabe den Strings "q" oder "ende" entspricht.

Der senkrechte Strich ( pipe genannt) fungiert als logisches oder ( or ). Die Zeilen 9 bis 12 könnte man auch schreiben als:

if (eingabe == "q") or (eingabe == "ende"):
    print("Spiel beendet")
    break

und man würde sich damit sogar eine Einrückungsebene sparen.

match kann aber noch ein wenig mehr, wie im nächsten case-Block (Zeile 13-17) demonstriert:

Hier wird der Textstring eingabe implizit in die integer-Variable x umgewandelt…aber nur wenn die Prüfung mittels .isdigit()' den Wert True` ergibt. Dies ist Äquivalent zu:

if eingable.isdigit():
    x = eingabe
    zahl = int(x)

Die Variable x bleibt auch nach dem Ende des match Blocks bestehen, falls wir sie danach noch brauchen sollten ( in diesem Beispiel wird sie aber nicht mehr gebraucht, sobald zahl berechnet wurde)

In Zeile 18 wird eingabe kurzerhand in die Variable wortumgewandelt, sofern der Wert von eingabe in der Liste wörter enthalten war.

In Zeile 20 fungiert der Unterstrich (_) als Auffangbecken für alle Fälle, die von keinem vorigen case Block behandelt wurden.

Warnung

Das folgende, komplexere Beispiel für eine match ... case Anweisung wird leichter verständlich wenn Sie die Kapitel Datenstrukturen und Funktionen breits gelesen haben. Andererseits: es ist ein funktionierendes Spiel und leicht erweiterbar! Starten Sie es und spielen Sie ein wenig damit herum…

Beispiel match2_de.py

Quellcode
 1# Textadventure, siehe https://peps.python.org/pep-0636/
 2dinge = {"Garten":["Rechen","Apfel","Birne"],
 3           "Haus":["Brot","Messer","Gabel","Teller"], 
 4         "Straße":[],  
 5        }
 6verbindungen = {"Straße": {"Süden":"Garten"},
 7                "Garten": {"Norden":"Straße", "Süden":"Haus"},
 8                "Haus":   {"Norden":"Garten"}
 9                }
10essbar = ("Brot","Apfel","Birne") 
11ort = "Haus"  
12was_ich_trage = [] # was ich trage
13zähler = 1
14print("Textadventure")
15while True:
16    print("---")
17    print("Ich bin jetzt hier:", ort)
18    print("Ich sehe hier folgende Gegenstände:", dinge[ort] )
19    print("mögliche Richtungen:", list(verbindungen[ort].keys()))   
20    eingabe = input(f"({zähler}): kommandos (z.B. hilfe) >>>").strip()
21    zähler += 1
22    eingabe = eingabe.replace(",", " ") # Ersetze Komma mit Leerzeichen 
23    match eingabe.split():  
24        case ["hilfe"]:
25            print("ich verstehe nur folgende Kommandos:")
26            print("nimm, benutze, wirf, gehe, status, ende")
27        case ["status"]:
28            print("ich halte/trage momentan:", was_ich_trage)
29        case ["ende"] | ["aufhören"] | ["hör", "auf"]:
30            break
31        case (["gehe", "nach", richtung] | ["gehe", richtung] |
32              [richtung]) if richtung in verbindungen[ort]:
33                ort = verbindungen[ort][richtung]
34                print("ich gehe nach", richtung)
35        case ["benutze", *gegenstände]:
36            for g in gegenstände:
37                if g not in was_ich_trage:
38                    print("ich habe kein",g)    
39                elif g in essbar:
40                    print("ich esse:", g)
41                    was_ich_trage.remove(g) # zerstört gegenstand
42                else:
43                    print("ich benutze:",g)                        
44        case (["nimm",  *gegenstände] | ["hebe", *gegenstände, "auf"] |
45              ["hebe", "auf", *gegenstände]):
46            for g in gegenstände:
47                if g in dinge[ort]:
48                    dinge[ort].remove(g)
49                    was_ich_trage.append(g)
50                    print("ich nehme", g) 
51                else: 
52                    print("da ist kein",g,"den ich nehmen könnte")          
53        case (["lass", "fallen", *gegenstände] | 
54              ["lass", *gegenstände, "fallen"] | ["wirf", *gegenstände]|
55              ["wirf", *gegenstände, "weg"]):
56            for g in gegenstände:
57                if g not in was_ich_trage:
58                    print("ich habe kein",g)
59                else:
60                    was_ich_trage.remove(g)
61                    dinge[ort].append(g)
62                    print("ich lasse fallen:", g)    
63        
64        case _:
65            print("ungültiges Kommando oder ungültige Aktion")
66print("Spiel beendet")

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
Textadventure
---
Ich bin jetzt hier: Haus
Ich sehe hier folgende Gegenstände: ['Brot', 'Messer', 'Gabel', 'Teller']
mögliche Richtungen: ['Norden']
(1): kommandos (z.B. hilfe) >>>nimm Brot
ich nehme Brot
---
Ich bin jetzt hier: Haus
Ich sehe hier folgende Gegenstände: ['Messer', 'Gabel', 'Teller']
mögliche Richtungen: ['Norden']
(2): kommandos (z.B. hilfe) >>>hebe Messer auf
ich nehme Messer
---
Ich bin jetzt hier: Haus
Ich sehe hier folgende Gegenstände: ['Gabel', 'Teller']
mögliche Richtungen: ['Norden']
(3): kommandos (z.B. hilfe) >>>hebe auf Gabel Teller
ich nehme Gabel
ich nehme Teller
---
Ich bin jetzt hier: Haus
Ich sehe hier folgende Gegenstände: []
mögliche Richtungen: ['Norden']
(4): kommandos (z.B. hilfe) >>>gehe nach Norden
ich gehe nach Norden
---
Ich bin jetzt hier: Garten
Ich sehe hier folgende Gegenstände: ['Rechen', 'Apfel', 'Birne']
mögliche Richtungen: ['Norden', 'Süden']
(5): kommandos (z.B. hilfe) >>>nimm Apfel Birne
ich nehme Apfel
ich nehme Birne
---
Ich bin jetzt hier: Garten
Ich sehe hier folgende Gegenstände: ['Rechen']
mögliche Richtungen: ['Norden', 'Süden']
(6): kommandos (z.B. hilfe) >>>Norden
ich gehe nach Norden
---
Ich bin jetzt hier: Straße
Ich sehe hier folgende Gegenstände: []
mögliche Richtungen: ['Süden']
(7): kommandos (z.B. hilfe) >>>status
ich halte/trage momentan: ['Brot', 'Messer', 'Gabel', 'Teller', 'Apfel', 'Birne']
---
Ich bin jetzt hier: Straße
Ich sehe hier folgende Gegenstände: []
mögliche Richtungen: ['Süden']
(8): kommandos (z.B. hilfe) >>>lass fallen Messer Gabel
ich lasse fallen: Messer
ich lasse fallen: Gabel
---
Ich bin jetzt hier: Straße
Ich sehe hier folgende Gegenstände: ['Messer', 'Gabel']
mögliche Richtungen: ['Süden']
(9): kommandos (z.B. hilfe) >>>iss Brot Apfel
ungültiges Kommando oder ungültige Aktion
---
Ich bin jetzt hier: Straße
Ich sehe hier folgende Gegenstände: ['Messer', 'Gabel']
mögliche Richtungen: ['Süden']
(10): kommandos (z.B. hilfe) >>>benutze Brot Messer Apfel
ich esse: Brot
ich habe kein Messer
ich esse: Apfel
---
Ich bin jetzt hier: Straße
Ich sehe hier folgende Gegenstände: ['Messer', 'Gabel']
mögliche Richtungen: ['Süden']
(11): kommandos (z.B. hilfe) >>>ende
Spiel beendet

Wie es funktionert

Zeile 2 bis 10 erstellt Datenstrukturen (mehr dazu im entsprechenden Kapitel dieses Buches) und zwar drei dictionaries. dinge hat Textstrings als keys und Listen als values (erkennbar an den eckigken Klammern). Der Key Straße hat als value eine leere Liste (die zwei eckigen Klammern). Auch wenn Sie jetzt noch nichts von Datenstrukturen wissen könnten Sie das Programm schon erweitern, indem Sie Zeilen einfügen mit neuen Orten ( der Text links vom Doppelpunkt und Listen mit Gegenständen an diesen Orten (Die Textstrings innerhalb der eckigen Klammern rechts vom Doppelpunkt).

Die verbindungen sind wiederum ein dictionary, diesmals allerdings mit dictionaries als values. Hier wird abgespeichert welcher Ort mit welchem anderen Ort verbunden ist. Ganz im Norden ist die Straße, südlich davon der Garten und ganz im Süden das Haus.

Das Tuple essbar enthält Gegenstände die der Spieler essen kann ( mit dem “benutzen” Kommando)

Die Variable ort enthält einen Textstring um anzuzeigen wo sich dier Spieler gerade befindet und was_ich_trage ist derzeit eine leere Liste.

Ab Zeile 15 geht es los: Des Spieler bekommt angezeigt wo er sich befindent, welche Gegenstände es dort gibt und in welche Richtungen er gehen kann.

In Zeile 20 wird die eingabe des Spielers wird mittels der Textfunktion .strip() von unnötigen Leerzeichen gereinigt (Leerzeichen am Anfang und am Ende des Strings werden entfernt).

In Zeile 22 wird die eingabe nocheinmal mit Hilfe der Textfunktion replace verändert: Alle Beistriche werden durch Leerzeichen ersetzt.

In Zeile 23 beginnt der match Block. Der auszuwertende Ausdruck ist eine Liste von Wörtern, erzeugt mittels der Textfunktion split(). Hat der Spieler z.B. den Textstring "Hebe Apfel auf" eingegeben so erzeugt split()daraus die _Liste_[“Hebe”, “Apfel”, “auf”]`.

Zeile 24 und Zeile 27 behandeln relativ einfache Fälle: Der Spieler hat nur ein einziges Wort eingegeben. Da split() immer eine Liste erzeugt, auch wenn nur ein einzelenes Element in der Liste drin ist, muss in den case-Ausdrücken ein eckiges Klammernpaar stehen.

Zeile 29 verbindet mehrere Ausdrücke mittels der Pipe (|) zu einer logischen oder Verknüpfung.

Zeile 31 wird richtig kompliziert: Wiederum erzeugt die Pipe eine oder Verknüpfung. Da die Zeile sehr lang wird habe ich sie in mehrere physikalische Zeilen unterteilt und runde Klammern verwendet. Hier zeigt sich eine der Stärken der match ... case Anweisung: Der Spieler z.B. kann Norden, gehe Norden oder gehe nach Norden eingeben, die case Anweisung wird in allen Fällen True und setzt den Wert der Variabel richtung auf "Norden". Dies funktioniert allerdings nur wenn der Wert von richtung im directory verbindungen des aktuellen ort’s enthalten ist.

Zeile 35 demonstriert den Stern-Präfix (*) in einem Case-Ausdruck: Sobald das erste Wort innerhalb der von eingabe.split() erzeugten Liste benutze ist, werden alle folgenden Worte in die Liste *gegenstände zusammengefaßt. In Zeile 36 wird daraufhin mit einer for-Schleife über alle Gegenstände iteriert:

for g in gegenstände:
    # ...

Hinweis

Das *-Präfix wird nur im Ausdruck des case-Blocks verwendet (*gegenstände) . Danach heißt die damit erzeugte Variable, über die iteriert werden kann, einfach gegenstände (ohne Stern davor).

Mehr zum Stern-Präfix gibt es im Kapitel Funktionen zu lesen, beim *args Parameter.

Die Zeilen 44, 53 demonstrieren das Stern-Präfix in Kombination mit einer Pipe (|) um logische oder Verknüpfungen zu erzeugen. Mittels if Befehl würde Zeile 44 lauten:

wortliste = eingabe.split() 
if len(wortliste) >= 1:
    if wortliste[0] == "nimm":
        gegenstände = wortliste[1:]
elif len(wortliste) >= 2:
    if (wortliste[0] == "hebe") and (wortliste[-1] == "auf"):
        gegenstände = wortliste[1:-1]
    elif (worstliste[0] == "hebe") and (wortliste[1] == "auf"):
        gegenstände = wortliste[2:]
for g in gegenstände:
    # ...

Man sieht daß die Verwendung von case hier kürzeren, eleganteren Code ermöglicht.

Zeile 63 fungiert als else Block und fängt alle Fälle auf, die nicht von einem vorherigen case-Block behandelt wurden.

Zusammenfassung#

Wir haben gesehen, wie man die drei Kontrollfluss-Anweisungen if, while und for zusammen mit den zugehörigen Anweisungen break und continue verwendet. Dies sind einige der am häufigsten verwendeten Teile von Python, und daher ist es unerlässlich, sich mit ihnen vertraut zu machen.

Als Nächstes werden wir sehen, wie man Funktionen erstellt und verwendet.