Python: While-Loop
Mit der Python While-Loop wird ein Code-Block wiederholt ausgeführt, solange eine Bedingung wahr ist. While-Schleifen kommen in Python in erster Linie zum Einsatz, wenn die Anzahl der benötigten Iterationen nicht vorab feststeht. Wir erklären, wie der While-Loop in Python funktioniert.
Lernen Sie mit unserem Python-Tutorial, selbstständig Python-Code zu schreiben.
Was ist die While-Loop in Python?
Bei der While-Schleife in Python handelt es sich um eine Kontrollstruktur. Kontrollstrukturen bestimmen darüber, welcher Code-Pfad zur Laufzeit beschritten wird. Schleifen kommen generell zum Einsatz, um einen Code-Block wiederholt auszuführen. Hier eine Übersicht wichtiger Kontrollstrukturen in Python:
Kontrollstruktur in Python | Erklärung |
---|---|
If-Else-Verzweigung | Führt Code-Block einmalig aus, sofern Kondition wahr ist. |
Python While-Schleife | Führt Code-Block wiederholt aus, solange Kondition wahr ist. |
Python For-Schleife | Iteriert über den Elementen einer Kollektion und führt dabei Code-Block für jedes Element aus. |
Typischerweise versuchen Programmierneulinge ohne Kenntnis von Schleifen, deren Funktionalität nachzubilden. Anstatt eine Datenstruktur mit mehreren Elementen zu definieren und mit einer Schleife zu verarbeiten, definieren sie eine eigene Variable für jeden Datensatz. Ferner wird der Code für die Verarbeitung der Datensätze dupliziert. Heraus kommt suboptimaler Code; hier dargestellt am Beispiel, drei Personen samt Namen und Alter auszugeben:
person1 = ('Jim', 17)
person2 = ('Jack', 42)
person3 = ('John', 63)
print(f"{person1[0]}, {person1[1]} years old")
print(f"{person2[0]}, {person2[1]} years old")
print(f"{person3[0]}, {person3[1]} years old")
Neben der unerwünschten Code-Duplizierung birgt dieser Ansatz ein logistisches Problem: Wenn die Anzahl der Datensätze erst zur Laufzeit feststeht, lassen sich die entsprechenden einzelnen Variablen nicht vorab definieren. Zur Lösung des Problems kommen Kollektionen von Elementen und Schleifen zu deren Verarbeitung zum Einsatz.
Wenn zur Laufzeit bekannt ist, wie viele Wiederholungen benötigt werden, liefert die For-Schleife in Python das beste Ergebnis. Beispielshalber bilden wir den Code zur Ausgabe von drei Personen samt Alter mittels For-Schleife ab. Der Code funktioniert ohne Duplizierungen und enthält unabhängig von der Anzahl der Datensätze nur zwei Variablen:
people = ('Jim', 'Jack', 'John')
ages = (17, 42, 63)
for person, age in zip(people, ages):
print(f"{person}, {age} years old")
Im Gegensatz zur For-Schleife kommt eine While-Loop in Python zum Einsatz, wenn nicht bekannt ist, wie viele Wiederholungen benötigt werden. Ein Beispiel ist der Austausch von Nachrichten über eine geöffnete Verbindung. Solange die Verbindung besteht, werden Nachrichten verarbeitet. Eine If-Anweisung im Schleifenkörper wertet ein Signal aus und beendet die Verbindung, wenn gewünscht:
while connection_open():
print('Ready to receive')
process_messages()
if should_close_connection():
close_connection()
# once loop terminates
print('Connection was closed')
Ferner kommen While-Schleifen für prinzipiell unlimitierte Wiederholungen zum Einsatz. Bekannte Beispiele sind Geldautomaten, die Linux-Eingabeaufforderung und die Python „Read-Eval-Print Loop“ (REPL). Hier schematisch dargestellt eine REPL-Implementation per While-Endlosschleife:
# Loop
while True:
# Read user input
user_input = read_input()
# Evaluate input and produce result
result = eval(user_input)
# Print result
print(result)
Wie funktioniert die While-Schleife in Python?
Die Python While-Schleife funktioniert ähnlich der If-Else-Verzweigung in Python. Beide Kontrollstrukturen bestehen aus zwei Teilen:
- einer Kondition, die ausgewertet wird
- einem Körper mit Anweisungen
Der Unterschied besteht darin, wie oft der Körper ausgeführt wird. Der Körper einer If-Anweisung wird höchstens einmal ausgeführt:
if condition:
run_once()
Im Unterschied zur If-Anweisung wird der Körper der While-Loop in Python ggf. mehrmals ausgeführt:
while condition:
run_again()
Wir halten das generelle Muster beim Durchlaufen einer Python While-Schleife fest:
- Die Kondition wird ausgewertet.
- Ist die Kondition wahr, wird der Schleifen-Körper ausgeführt.
- Die Kondition wird erneut ausgewertet:
- Ist die Kondition weiterhin wahr, wiederholt sich der Prozess.
- Ist die Kondition unwahr, terminiert die Schleife.
Ähnlich der If-Anweisung kann eine While-Schleife in Python über einen optionalen Else-Block verfügen. Der Else-Block wird einmalig ausgeführt, sollte die Kondition unwahr sein bzw. werden:
while False:
# this code doesn't loop
never_runs()
else:
# instead, this code runs once
runs_once()
Wie unterscheiden sich For- und While-Schleife in Python?
Die While-Loop ist auch in Python verwandt mit der For-Schleife. Beide führen einen Code-Block wiederholt aus. Man spricht dabei auch von „Iterationen“ bzw. „iterieren“. Der Unterschied liegt in der Anzahl der Wiederholungen.
In Python kommen For-Schleifen in erster Linie zum Einsatz, um über den Elementen einer Kollektion zu iterieren. Dabei ist die maximale Anzahl an Iterationen durch die Länge der Kollektion begrenzt. Wir iterieren über den Buchstaben des Worts „Python“ und geben jeden Buchstaben einzeln aus:
for letter in 'Python':
print(letter)
Die Python While-Schleife ist nicht auf das Iterieren über Kollektionen spezialisiert und lässt sich flexibel einsetzen. Generell handelt es sich bei der While-Schleife um das grundlegende Schleifenkonstrukt. Die Funktionalität einer For-Schleife lässt sich mit Hilfe einer While-Schleife konstruieren; andersherum ist dies nicht der Fall.
Schauen wir uns ein paar Beispiele an. Wir bilden die Funktionalität einer herkömmlichen For-Schleife mit numerischer Schleifenvariable als Python While-Schleife nach. Dazu definieren wir außerhalb der Schleife eine Counter-Variable und inkrementieren deren Wert innerhalb des Schleifenkörpers:
counter = 0
limit = 10
while counter < limit:
print(counter)
counter += 1
Die äquivalente For-Schleife ist kürzer und direkter:
for counter in range(10):
print(counter)
Ähnlich verhält es sich, wenn wir eine While-Schleife nutzen, um über den Buchstaben eines Worts zu iterieren. Wir nutzen einen Iterator und die next()-Funktion. Ist der Iterator verbraucht, wird statt einem Buchstaben None zurückgegeben; die Schleife terminiert. Der dabei herauskommende Code ist deutlich komplizierter als die äquivalente For-Schleife. Offensichtlich ist die While-Schleife in Python für die Lösung dieses Problems nicht das optimale Werkzeug:
word = 'Python'
letters = iter(word)
letter = ''
while letter is not None:
letter = next(letters, None)
if letter:
print(letter)
Vorsicht mit While-Endlosschleifen in Python
Insbesondere sind While-Schleifen in Python interessant, um Endlosschleifen zu implementieren. Das mag zunächst widersinnig erscheinen. Denn versehentlich produzierte Endlosschleifen sind gefürchtet. Die Kondition wird nie unwahr; das Programm bleibt hängen:
while True:
print("Forever…")
In der Tat existieren vielfältige Anwendungsfälle für beabsichtigte Endlosschleifen. Eine versehentlich produzierte While-Endlosschleife wird in der Regel durch einen Ausdruck verursacht, der immer zu True evaluiert. Hier ein Beispiel:
while 1 == 1 + 1 - 1:
print("And ever…")
Finden Sie sich im Python-REPL in einer While-Endlosschleife gefangen, hilft die Tastenkombination Ctrl + C bzw. Strg + C weiter. Diese sendet ein Stopp-Signal an den Python-Interpreter, der die Ausführung der Schleife abbricht.
Durchläufe einer While-Loop in Python abbrechen und überspringen
Generell iteriert eine While-Schleife, bis die Loop-Bedingung unwahr wird. Ein gebräuchlicher Trick besteht darin, eine „Flag“-Variable als Bedingung einzusetzen. Dazu wird außerhalb der Schleife eine boolesche Variable definiert und in der Loop-Bedingung ausgewertet. Bei Erreichen einer gewissen Bedingung innerhalb des Schleifenkörpers stellen wir die Flag scharf. Beim Auswerten der Kondition vor dem nächsten Durchlauf führt der neue Wert zum Beenden der Schleife:
aborted = False
while not aborted:
print("Still going…")
if some_cond:
# will prevent next iteration
aborted = True
Dieses Muster findet sich häufig, ist jedoch nicht besonders elegant. Was, wenn im Schleifenkörper nach dem Scharfstellen der Flag-Variable weiterer Code folgt? Dieser müsste übersprungen werden. Praktischerweise kennt Python in While-Schleifen die Break-Anweisung.
Wird innerhalb einer Schleife eine Break-Anweisung ausgeführt, terminiert die Schleife sofort. Damit ist die Break-Anweisung in Schleifen ähnlich der Return-Anweisung in Funktionen. Jedoch gibt break keinen Wert zurück. Es ist gebräuchlich, die Break-Anweisung zum Abbrechen einer Endlosschleife einzusetzen:
while True:
print("Still going…")
if some_cond:
break
# we never get here
print("We shouldn't be here")
# we end up here after breaking
print("Done.")
Konzeptuell verwandt mit der Break-Anweisung ist die Continue-Anweisung. Wird eine Continue-Anweisung im Schleifenkörper ausgeführt, wird der nachfolgende Code übersprungen. Weiter geht es mit der nächsten Iteration. Mit break und continue lassen sich einfache, textbasierte Menüs implementieren, wie man sie aus frühen Computerspielen kennt:
# `continue` takes us here
while True:
print("Press G to start a new game")
print("Press S to see stats")
print("Press M for main menu")
print("Press Q to quit")
key_press = input("Your choice \n")[0].upper()
print(f"You pressed {key_press}")
if key_press == "G":
# start game routines
print("Starting game …")
elif key_press == "S":
# show stats
print("Showing stats …")
elif key_press == "M":
# back to main menu
print("Returning to menu")
continue
elif key_press == "Q":
# break out of loop
print("Quitting")
break
else:
print("Unknown command. Try again")
# `break` takes us here
...
Aus verschachtelten Python While-Schleifen ausbrechen
Der Einsatz ineinander verschachtelter Schleifen führt schnell zu Verwirrungen. Dann ist es hilfreich, mittels Break-Anweisung aus der zuletzt gestarteten Schleife auszubrechen. Was jedoch, wenn wir im selben Zug eine darüberliegende Schleife verlassen möchten? Für diesen Fall existiert kein eigenes Schlüsselwort; prinzipiell funktioniert eine Lösung mittels Flag-Variable.
Eleganter lässt sich aus verschachtelten While-Loops in Python ausbrechen, indem break, continue und else clever kombiniert werden. Wir bauen unser Schleifenkonstrukt so, dass die äußere Schleife abgebrochen wird, wenn in der inneren Schleife eine Break-Anweisung ausgeführt wird. Dazu nutzen wir eine Continue-Anweisung innerhalb des inneren Else-Blocks, um das äußere break bei Bedarf zu überspringen:
# `continue` takes us here
while outer_cond:
while inner_cond:
...
if some_cond:
print("Breaking out of inner loop")
break
# no inner `break` occured
else:
print("Continuing outer loop")
# skip rest of outer loop body
continue
# we only get here if inner `break` occured
print("Breaking out of outer loop")
break
# outer `break` takes us here
...
Wie lässt sich die While-Schleife in Python verwenden?
In der Praxis finden sich vielfältige Anwendungsfälle für den Einsatz der While-Loop in Python. Generell werden While-Schleifen für Algorithmen verwendet, bei denen die Anzahl der Wiederholungen vorher nicht feststeht bzw. sich während der Ausführung ändert. While-Schleifen werden häufig in Kombination mit weiteren Kontrollstrukturen wie Verzweigungen und Try-Else-Anweisungen eingesetzt. Betrachten wir ein paar Beispiele.
In Python mit While-Schleife eine Kollektion konsumieren
Um über den Elementen einer Kollektion zu iterieren, bietet sich in Python die For-Schleife an. Zumindest ist dies der Fall, sofern wir die Kollektion nicht aus dem Schleifenkörper heraus verändern. Was jedoch, wenn wir beim Durchlaufen der Elemente Änderungen vornehmen? Stellen wir uns vor, wir wollten beim Iterieren Elemente der Kollektion entfernen. Man spricht in diesem Fall davon, dass die Kollektion „konsumiert“ wird.
For-Schleifen können seltsame Fehler verursachen, wenn sich die unterliegende Kollektion während des Iterierens ändert. Möchten wir eine Kollektion konsumieren, eignet sich in Python die While-Schleife. Wir nutzen die Kollektion direkt als Bedingung der While-Loop. Solange die Kollektion Elemente enthält, wird sie im booleschen Kontext als wahr ausgewertet. Ist die Kollektion leer, bricht die Schleife ab:
pieces = ['x', 'o', 'o', 'o', 'x', 'o', 'x', 'x']
while pieces:
piece = pieces.pop()
print(f"Removed {piece}")
# test
assert pieces == []
Mit While-Loop in Python eigene range()-Funktion implementieren
Mit While-Loops in Python lassen sich sogenannte Generatoren implementieren. Ein Generator ist eine Funktion, die die Yield-Anweisung nutzt und auf Anfrage Werte generiert. Wir schreiben eine eigene Implementation der range()-Funktion. Dabei nutzen wir die Yield-Anweisung innerhalb einer While-Schleife, um fortlaufende Zahlen zu generieren. Beim Erreichen der Yield-Anweisung wird ein Wert ausgegeben und die Schleife pausiert:
def my_range(start, stop):
# only positive ranges implemented
if stop <= start:
return None
current = start
while current < stop:
yield current
# next call of next() continues here
current += 1
# test
assert list(my_range(7, 9)) == list(range(7, 9))
Mit Python While-Schleife ein Modell optimieren
Das Optimieren von Modellen gehört zum Standardrepertoire wissenschaftlicher Disziplinen. Dabei wird basierend auf einer Menge an Parametern ein Modell berechnet. Im Anschluss werden die Parameter angepasst und das Modell erneut berechnet. Mit einer Zielfunktion wird abgeschätzt, ob die Änderung der Parameter ein besseres Modell ergeben hat. Ist dies der Fall, wird der Prozess wiederholt. So lassen sich iterativ optimale Parameter für das Modell finden.
Normalerweise pendelt sich das Modell nach einigen Wiederholungen ein, so dass der Fortschritt immer kleiner ausfällt. Fällt der Fortschritt unter einen bestimmten Schwellenwert, brechen wir die Optimierung ab. Um sicherzustellen, dass die Schleife terminiert, begrenzen wir zusätzlich die maximale Anzahl der Durchläufe. Hier schematisch ein Ansatz zur Modelloptimierung unter Nutzung einer Python While-Loop:
limit = 5
round = 0
progress = True
while progress and round < limit:
# attempt next optimization
round += 1
# compute optimized parameters
params = optimize(params)
# make a copy of the old model
old_model = model
# compute new model using optimized parameters
model = run(model, params)
# worthwhile to further optimize?
progress = has_improved(model, old_model)
Verbindungsaufbau in Python mit While-Schleife und Try-Except implementieren
Der Aufbau einer Verbindung kann fehlschlagen. Daher ist es wünschenswert, den Verbindungsaufbau mit mehreren Versuchen zu implementieren. Da wir vorab nicht wissen können, wie viele Versuche benötigt werden, nutzen wir in Python eine While-Schleife. Ferner begrenzen wir die maximale Anzahl an Versuchen. War keiner der Versuche erfolgreich, brechen wir mit einer Fehlermeldung ab.
Hier ein schematischer Lösungsansatz: Wir nutzen eine Try-Except-Anweisung, um einen Fehler beim Verbindungsaufbau abzufangen. Der Einsatz einer Break-Anweisung im Try-Block sowie der einer Continue-Anweisung im Except-Block stellen sicher, dass wir korrekt iterieren. Schlägt der Verbindungsaufbau fehl, versuchen wir es mit continue erneut. Wurde die Verbindung hergestellt, beenden wir die Schleife mit break:
max_tries = 10
attempt = 0
conn = None
# `continue` takes us here
while attempt < max_tries:
attempt += 1
print("Trying to get a connection")
try:
# might raise `ConnectionException`
conn = get_connection()
# got our connection
break
# `get_connection()` raised `ConnectionException`
except ConnectionException:
print("Something went wrong. Trying again")
continue
# went through `max_tries` unsuccessful connection attempts
else:
assert conn is None
print("Unable to connect")
# `break` takes us here
assert conn is not None
print("Connection established")
Mit der Python While-Schleife über rekursiven Strukturen iterieren
Die While-Schleife in Python ist gut geeignet für Lösungen rekursiver Probleme. So eignet sich die Loop zum Iterieren über:
- verschachtelten Listen
- Baumstrukturen
- Graphen
Schauen wir uns am Beispiel einer Matrjoschka-Puppe an, wie das funktioniert. Das bekannte Spielzeug besteht aus ineinander verschachtelten Püppchen. Von außen ist die Anzahl der Ebenen nicht einsehbar. Stattdessen gehen wir iterativ vor: Wir öffnen das äußerste Püppchen und schauen, was wir im Inneren finden. Handelt es sich um eine weitere Matrjoschka, wiederholen wir den Prozess – ein typischer Fall für den Einsatz einer While-Schleife.
Wir modellieren die Matrjoschka als verschachtelte Liste mit jeweils einem einzigen Element. Entweder ist eine weitere Liste enthalten oder ein Objekt, das keine Liste ist. Wir iterieren über der Matrjoschka, solange diese eine Liste ist. Innerhalb des Schleifenkörpers nutzen wir eine Zuweisung, um eine Ebene tiefer zu gehen. Irgendwann finden wir ein Element, das keine Liste ist. Dann haben wir das enthaltene Objekt gefunden und brechen die Iteration ab:
def open_matroshka(matroshka):
"""
* Matroshka dolls stacked five levels deep, with `None` inside:
`matroshka = [[[[[None]]]]]`
"""
while type(matroshka) is list:
print("Opening the next matroshka")
# go one level deeper
matroshka = matroshka.pop()
else:
print(f"Reached the bottom and found {matroshka}")
return matroshka
# test
matroshka = [[[[[None]]]]]
assert open_matroshka(matroshka) is None
Dieser simple Ansatz funktioniert unabhängig von der Tiefe der verschachtelten Püppchen. Unser Algorithmus gräbt sich bis zum Boden durch und bringt das enthaltene Objekt zum Vorschein. Die Magie der Python While-Schleife in Aktion.
Mit einer Python While-Schleife auf einem Spielbrett laufen
Ein häufiges Einsatzszenario für While-Schleifen in Python besteht darin, eine Figur auf einem Spielbrett zu bewegen. Möchte man garantiert alle Felder besuchen, werden zwei ineinander verschachtelte Schleifen benötigt. Dabei gilt es, die Laufzeit zu beachten: Bei großen Räumen kann das Programm u. U. sehr lange laufen.
Wir implementieren einen simplen „Random Walk“, bei dem eine Figur auf dem Spielbrett so lange zufällig bewegt wird, bis sie sich an einem Zielort befindet. Ein solches Bewegungsmuster findet sich beispielsweise bei der Bewegung eines Partikels in Flüssigkeit oder einer Fliege, die im Raum herumfliegt. Da vorher nicht bekannt ist, wie viele Iterationen benötigt werden, setzen wir eine Python While-Loop ein.
Wir definieren zunächst die random_walk()-Funktion, die die While-Schleife enthält. Mit dem Python-Operator für „Ist-Gleich“ überprüfen wir, ob die derzeitige Position das Ziel ist. Wenn nicht, wird weiter iteriert:
def random_walk(board = (10, 10), goal = (4, 4), start = (0, 0)):
# ensure arguments are valid
if not (goal[0] in range(board[0]) and goal[1] in range(board[1]) and start[0] in range(board[0]) and start[1] in range(board[1])):
print(f"Goal {goal} and / or start position {start} outside of board with dimensions {board}")
return None, 0
steps = 0
pos = start
# as long as we haven't reached the goal
while not pos == goal:
# move to neighboring position
pos = get_neighbor(pos, board)
steps += 1
print(f"Moved to position {pos}")
print(f"Reached goal at {pos} after {steps} steps")
return pos, steps
Ferner definieren wir eine Hilfsfunktion get_neighbor(), die ein mögliches Feld um eine bestimmte Position herum zurückgibt:
def get_neighbor(pos, bounds):
from random import choice
"""
x = 0 . . . m
- - - - - - - -
y = 0 |
|
. | (x, y-1)
. | (x-1, y) (x, y) (x+1, y)
. | (x, y+1)
|
n |
"""
x, y = pos
# computer neighbors
neighbors = [ (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1) ]
# filter out neighbors that are outside of bounds
neighbors = [ pos for pos in neighbors if 0 <= pos[0] < bounds[0] and 0 <= pos[1] < bounds[1] ]
# select a random neighbor
neighbor = choice(neighbors)
return neighbor
Im Anschluss testen wir unsere Random-Walk-Implementation:
random_walk(board = (10, 10), goal = (4, 4), start = (5, 7))