Python: For-Loop verstehen und anwenden

Die For-Loop in Python, zu Deutsch „For-Schleife“, dient zum wiederholten Ausführen eines Code-Blocks. For-Schleifen sind grundlegender Bestandteil der meisten Programmiersprachen. Wir zeigen, wie die For-Loop in Python funktioniert und wie sie eingesetzt wird.

Domain Bundle Aktion

Nur für kurze Zeit: So erhalten Sie zwei Gratis-Domains!

Kaufen Sie eine .de-Domain und erhalten eine .com und .info Domain gratis dazu.

E-Mail-Postfach
Wildcard SSL
Persönlicher Berater

Was ist die For-Schleife in Python?

Die For-Schleife ist neben der if-else-Verzweigung das wohl bekannteste Programmierkonstrukt. Generationen von Studierenden haben sich am informatischen Konzept der Schleife das Hirn zermartert. Denn Schleifen sind zunächst wenig intuitiv. Dabei entstehen die Verständnisprobleme möglicherweise mehr durch die Art, wie die Materie präsentiert wird. Denn an sich sind Schleifen nichts Außergewöhnliches.

Verdeutlichen wir uns das Konzept der Schleife an einem anschaulichen Beispiel. Stellen wir uns eine Schulklasse vor. Die Lehrkraft möchte im Rahmen eines Experiments die durchschnittliche Größe der Kinder ermitteln. Dazu fragt sie nach und nach jedes Kind nach dessen Größe und addiert die einzelnen Größen zu einer laufenden Summe. Im Anschluss teilt sie die Summe durch die Anzahl der Kinder, um die durchschnittliche Größe zu erhalten. Wir abstrahieren einen simplen Algorithmus aus dem Vorgehen der Lehrkraft:

  1. Größe jedes Kindes abfragen und zu laufender Summe addieren
  2. Summe durch Anzahl der Kinder teilen

Der erste Schritt wird für jedes Kind einmal ausgeführt und ist damit abhängig von der zahlenmäßigen Größe der Klasse. Der zweite Schritt wird unabhängig von der Größe der Klasse nur einmal ausgeführt. Eine Schleife benötigen wir für den ersten Schritt. Hier ein Code-Beispiel, das die Durchschnittsgröße eines Schulkinds mit einer For-Schleife in Python berechnet:

children_heights = [155, 171, 148, 161, 158, 153, 162]
height_sum = 0

for height in children_heights:
    height_sum = height_sum + height

average_height = height_sum // len(children_heights)
print(average_height)

Die For-Schleife in Python führt einen Code-Block wiederholt aus. Man spricht dabei auch von „Iteration“. Insbesondere erlauben Schleifen, mehrere in einer „Kollektion“ zusammengefasste Elemente einzeln nach demselben Schema zu verarbeiten. Eine For-Schleife nutzen wir dann, wenn sich der Umfang der Kollektion zur Laufzeit des Programms feststellen lässt. Ist dies nicht der Fall, kommt für gewöhnlich eine While-Schleife zum Einsatz.

Tipp

Lernen Sie, selbstständig Python-Code zu schreiben – mit unserem Python-Tutorial!

Was unterscheidet die For-Schleife in Python von anderen Sprachen?

Viele Programmiersprachen kennen das Konzept der For-Schleife. Neben Python ist diese auch in anderen Sprachen wie C, Java, JavaScript und PHP ein grundlegendes Konstrukt. Rein funktionale Sprachen wie Haskell oder Lisp kommen für gewöhnlich ohne explizite For-Schleife aus. Anstelle von Iteration werden in diesen Sprachen rekursive Funktionen eingesetzt.

Alle Schleifen haben gemeinsam, dass ein Code-Block wiederholt ausgeführt wird. Jedoch unterscheidet sich der Wirkmechanismus der For-Schleife in Python deutlich von anderen Sprachen. So kommt bei den meisten Programmiersprachen eine sogenannte Schleifen-Variable zum Einsatz, die beim Durchlaufen der Schleife hochgezählt (inkrementiert) bzw. runtergezählt (dekrementiert) wird.

Operation Bedeutung Herkömmliche Syntax Python-Syntax
Inkrementieren Wert einer Ganzzahl-Variable um bestimmten, festen Betrag erhöhen i++ index += 1
Dekrementieren Wert einer Ganzzahl-Variable um bestimmten, festen Betrag verringern i-- index -= 1

Betrachten wir an einem Beispiel, wie eine For-Schleife in anderen Sprachen funktioniert. Wir geben die Zahlen von 0 bis 9 in JavaScript mit einer For-Schleife aus. Wir definieren eine Ganzzahl-Variable 'number' und inkrementieren diese, solange die enthaltene Zahl kleiner als 10 ist. Der dabei zum Einsatz kommende Code wirkt auf Neulinge eher kryptisch:

for ( let number = 0; number < 10; number++ ) {
    console.log(number);
}

Der Code für eine korrespondierende For-Schleife in Python wirkt deutlich aufgeräumter:

for number in range(10):
    print(number)

Anstatt den Wert der Schleifen-Variable direkt auszugeben, wird dieser in der Praxis meist zum Indizieren eines Elements innerhalb einer Kollektion genutzt. Wiederum zunächst ein Beispiel in JavaScript: Wir geben die in der Liste 'people' enthaltenen Namen nacheinander aus. Dabei nutzen wir die Schleifen-Variable 'i' als fortlaufenden Index der einzelnen Elemente:

people = ['Jack', 'Jim', 'John']
for (let i = 0; i < people.length; i++) {
    console.log("Here comes " + people[i]);
}

Das direkte Indizieren sukzessiver Listen-Elemente ist mit Vorsicht zu genießen. Denn ein versuchter Zugriff außerhalb erlaubter Grenzen führt zu einem Laufzeitfehler. In der Regel handelt es sich dabei um den berüchtigten „Off-by-one Error“. Python macht vor, dass es auch anders geht. Hier dasselbe Beispiel mit einer For-Loop in Python – wir iterieren direkt über den Listen-Elementen, ohne diese mit einer Schleifen-Variable zu indizieren:

people = ['Jack', 'Jim', 'John']
for person in people:
    print(f"Here comes {person}")

Der indirekte Nutzen der Schleifen-Variable trägt viel zur Verwirrung beim Lernen von For-Schleifen in anderen Sprachen bei. Denn die dort zentrale Schleifen-Variable interessiert uns meist nicht. Sie dient lediglich zum Indizieren der einzelnen Elemente. Die Nutzung herkömmlicher For-Schleifen bedingt ein Verständnis mehrerer komplexer Themen. Am Beispiel von JavaScript:

Thema Auftreten in JavaScript-For-Schleife
Variablen-Zuweisung let i = 0
Boolesche Ausdrücke i < limit
Inkrement-/Dekrement-Operator i++ / i--
Feststellen der Größe einer Kollektion i < list.length
Null-basierte Indizierung von Elementen i < list.length ist OK; i <= list.length führt zu Off-by-one Error

Die For-Schleife in Python ist deutlich benutzerfreundlicher. Auch wenn mit 'for' dasselbe Schlüsselwort zum Einsatz kommt, handelt es sich um einen grundlegend anderen Ansatz. Anstatt eine Schleifen-Variable zu inkrementieren und damit sukzessive Elemente zu indizieren, iterieren wir direkt über den Elementen einer Kollektion. Die For-Loop in Python ist damit vergleichbar mit dem forEach-Konstrukt mancher Sprachen wie Ruby und JavaScript.

Geben wir zur Veranschaulichung die einzelnen Buchstaben eines Wortes aus. Innerhalb der For-Schleife wird pro Schleifen-Durchlauf ein Buchstabe des Wortes in der Variable 'letter' zur Verfügung gestellt. Das funktioniert ohne Schleifen-Variable und damit ohne Gefahr, einen Off-by-one Error zu produzieren. Der Code ist präzise und leicht lesbar:

word = "Python"
for letter in word:
    print(letter)

Wie funktioniert eine For-Schleife in Python?

Wie wir gesehen haben, löst die For-Schleife in Python elegant das Problem, über den Elementen einer Kollektion zu iterieren. Dabei kommen wir ohne den Umweg über eine numerische Schleifenvariable aus. Das ist toll, aber wie funktioniert das genau? Um das Wirkprinzip der For-Loop in Python zu verstehen, muss man die Konzepte des Iterable und Iterators kennen.

Worum handelt es sich bei Iterable, Iterator und Generator?

Die For-Schleife in Python operiert auf Objekten, die als „Iterables“ bekannt sind. Dabei handelt es sich um Strings, Listen, Tupel und weitere, zusammengesetzte Datentypen. In den Worten der offiziellen Python-Dokumentation:

    Zitat

    „[An iterable is] an object capable of returning its members one at a time“ - Quelle: docs.python.org/3/glossary.html

    Übersetzung: „[Ein Iterable ist] ein Objekt mit der Kapazität, seine Elemente einzeln zurückzugeben“ (übersetzt von IONOS)

    Ein Iterable-Objekt hat zwei Eigenschaften:

    1. Es fasst mehrere Elemente als Kollektion zusammen.
    2. Es gewährt Zugriff auf die Elemente über eine „Iterator“ genannte Schnittstelle.

    Zusammengenommen bedeutet dies, dass Iterables Collections sind, über deren Inhalten sich iterieren lässt. Spezifisch besitzt ein Iterable eine Methode '__iter__()', die einen Iterator zurückgibt. Beim Iterator handelt es sich um ein Objekt, das auf Kommando das nächste Element des Iterable liefert. Ferner erinnert ein Iterator die Position des zuletzt zurückgegebenen Elements innerhalb der Kollektion.

    Funktion Erklärung Beispiel
    iter(collection) Ruft Methode __iter__() der Collection auf it = iter("Python")
    next(iter) Ruft Methode __next__() des Iterators auf next(it)
    collection[index] Ruft Methode __getitem__(index) der Collection auf 'Python'[1]

    Ein Iterator liefert beim Aufruf der __next__()-Methode das nächste Element zurück. Wurden alle Elemente der Collection ausgeliefert, ist der Iterator erschöpft. Ein weiterer Aufruf von __next__() löst eine 'StopIteration'-Exception aus.

    Betrachten wir die Funktionsweise eines Iterators an einem Beispiel. Wir erzeugen ein range()-Objekt, das die konsekutiven Zahlen 21 bis 23 repräsentiert. Im Anschluss erzeugen wir einen Iterator mit der iter()-Funktion und geben sukzessive Elemente mit der next()-Funktion aus. Beim letzten Aufruf wird die Exception ausgelöst, da der Iterator erschöpft ist:

    numbers = range(21, 24)
    number = iter(numbers)
    next(number)
    # returns `21`
    next(number)
    # returns `22`
    next(number)
    # returns `23`
    next(number)
    # raises `StopIteration` exception

    Ein Iterator gibt Zugriff auf einzelne Elemente einer Collection. Daneben kennt Python das verwandte Konzept des „Generator“. Der Unterschied liegt darin, dass ein Generator einzelne Elemente erst beim Zugriff erzeugt. Dies spart vor allem Speicherplatz bei der Ausführung des Programms; man spricht dabei auch von „lazy generation“.

    Ein Generator in Python beruht auf einer Funktion, die die Yield-Anweisung benutzt. Diese gibt ähnlich der Return-Anweisung ein Objekt zurück und beendet den Funktionsaufruf. Beim erneuten Aufruf beginnt die Generator-Funktion jedoch nicht wieder von vorne, sondern läuft nach der letzten Yield-Anweisung weiter.

    Schauen wir uns ein Beispiel an. Wir schreiben eine eigene Implementation der range()-Funktion. Dabei nutzen wir die Yield-Anweisung innerhalb einer While-Schleife, um fortlaufende Zahlen zu generieren:

    def my_range(start, stop):
        if stop < start:
            return None
        current = start
        while current < stop:
            yield current
            current += 1
    
    # test
    assert list(my_range(7, 9)) == list(range(7, 9))

    Einen Durchlauf der For-Schleife in Python überspringen und abbrechen

    In der Praxis ist es manchmal notwendig, einen einzelnen Schleifen-Durchlauf zu überspringen. Wie viele andere Sprachen enthält Python die Continue-Anweisung. Beim Aufruf von continue innerhalb des Schleifenkörpers wird die laufende Iteration abgebrochen. Die Schleife beginnt im Anschluss sofort mit der nächsten Iteration.

    Eine Continue-Anweisung lässt sich ähnlich dem Early Return beim Funktionsaufruf nutzen. Beispielsweise überspringen wir eine Iteration, sobald wir feststellen, dass ein Datensatz nicht über die benötigte Qualität verfügt:

    def process_data(data):
        for data_set in data:
            data_set.validate()
            # early continue after cheap check fails
            if not data_set.quality_ok():
                continue
            # expensive operation guarded by early continue
            data_set.process()

    Ein weiteres Beispiel – wir geben einen Text aus und überspringen jeden zweiten Buchstaben:

    text = 'Skipping every second letter'
    for index, letter in enumerate(text):
        if index % 2 != 0 and letter != ' ':
            continue
        print(letter)

    Neben der Continue-Anweisung zum Überspringen eines Schleifen-Durchlaufs gibt es die Break-Anweisung. Ein Aufruf von break innerhalb des Schleifenkörpers bricht die weitere Ausführung der Schleife sofort ab. Damit hat break eine ähnliche Funktion für Schleifen wie die Return-Anweisung für Funktionen.

    Häufig kommt die Break-Anweisung zum Einsatz, um Suchalgorithmen zu implementieren. Wurde innerhalb einer Schleife ein gesuchtes Element gefunden, ist es unnötig, weiter zu iterieren. Überprüfen wir analog zur any()-Funktion eine Liste auf Vorhandensein eines einzelnen True-Wertes. Wir brechen mit break ab, sobald wir fündig geworden sind:

    bool_list = [False, False, True, False]
    for index, boolean in enumerate(bool_list):
        if boolean:
            print(f"Value at position {index + 1} is True")
            print(f"Aborting inspection of remaining {len(bool_list) - index - 1} item(s)")
            break

    Im Zusammenhang mit der Break-Anweisung lässt sich eine For-Schleife in Python mit einem optionalen Else-Körper versehen. Der enthaltene Code wird ausgeführt, wenn die Schleife terminiert, ohne dass eine Break-Anweisung ausgeführt wurde:

    def find_element(target, collection):
        for element in collection:
            if element == target:
                print("Found what you're looking for")
                break
        else:
            print("Didn't find what you were looking for")
    
    # test
    find_element('a', 'Python')
    find_element('o', 'Python')

    For-Schleifen kommen in Python oft innerhalb von Funktionskörpern zum Einsatz. In diesem Fall ist es gebräuchlich, statt einer Break-Anweisung eine Return-Anweisung zu verwenden. Unser Suchalgorithmus umformuliert ohne den Einsatz von break und else:

    def find_element(target, collection):
        for element in collection:
            if element == target:
                print("Found what you're looking for")
                # returning breaks us out of the loop
                return element
        # we made it here without returning
        print("Didn't find what you were looking for")
        return None
    
    # test
    print(find_element('a', 'Python'))
    print(find_element('o', 'Python'))

    Was sind die Best Practices für For-Loops in Python?

    For-Loops in Python dienen in erster Linie zum Iterieren über den Elementen einer Sequenz oder Kollektion. Ferner existieren für viele häufige Anwendungsfälle direktere Methoden. Wir stellen wichtige Best Practices und Anti-Patterns vor. Zunächst ein Überblick zentraler Begriffe:

    Begriff Erklärung Beispiel
    Collection Zusammenfassung mehrerer Elemente. Eine Collection ist ein Iterable ('Walter', 'White'), [4, 2, 6, 9], 'Python'
    Iterator Schnittstelle zum Iterieren über Collections it = iter('Python')
    Generator Eine Funktion, die yield statt der Return-Anweisung verwendet. Ein Generator ist Iterable range(10)
    Comprehension Iterierender Ausdruck; erzeugt eine neue Kollektion basierend auf einem Iterable [num ** 2 for num in range(10)]

    Direkt über den Elementen einer Kollektion iterieren

    Ein häufiger Fehler unerfahrener Python-Programmierer besteht darin, die For-Loop in Python zu missbrauchen. Wie in anderen Sprachen üblich, nutzen sie die len()-Funktion als Limit der range()-Funktion, um eine numerische Schleifen-Variable zu erzeugen. Sie indizieren damit die einzelnen Elemente der Kollektion:

    word = 'Python'
    for i in range(len(word)):
        print(word[i])

    Dieses Anti-Pattern ist aus gutem Grund als unpythonisch verpönt. Denn es ist besser, mit der For-Loop in Python direkt über den Elementen der Kollektion zu iterieren:

    word = 'Python'
    for letter in word:
        print(letter)

    Elemente einer Kollektion mit enumerate() samt Index aufzählen

    Manchmal benötigt man den Index eines Elements innerhalb der Collection. Anstatt den Index als Schleifen-Variable zu erzeugen, nutzen wir die enumerate()-Funktion. Diese gibt das Tupel (Index, Element) zurück. Dabei ist zu beachten, dass der Index bei null zu zählen anfängt:

    names = ["Jim", "Jack", "John"]
    for index, name in enumerate(names):
        print(f"{index + 1}. {name}")

    Mit der zip()-Funktion über Tupeln von Elementen iterieren

    Ein weiteres häufig anzutreffendes Szenario ist das gleichzeitige Iterieren über den Elementen zwei gleich langer Collections. Der pythonische Ansatz benutzt die zip()-Funktion. Diese nimmt zwei gleich lange Collections entgegen und gibt sukzessive 2-Tupel zurück:

    people = ('Jim', 'Jack', 'John')
    ages = (42, 69, 13)
    # ascertain both collections are same length
    assert len(people) == len(ages)
    # iterate over tuples of (person, age)
    for person, age in zip(people, ages):
        print(f"{person} is {age} years old")

    Mit der range()-Funktion eine numerische Schleifen-Variable erzeugen

    Normalerweise werden For-Loops in Python eingesetzt, um über Elementen einer Kollektion zu iterieren. Das Inkrementieren einer Ganzzahl mit einer For-Schleife ist in Python eher ein Spezialfall. Der korrekte Weg besteht darin, mit der range()-Funktion ein Range-Objekt zu konstruieren und über diesem zu iterieren:

    for counter in range(10):
        print(counter)

    Mit dem in-Operator testen, ob eine Kollektion ein Element enthält

    Das Auffinden eines bestimmten Elements innerhalb einer Kollektion gehört zum Standard-Repertoire eines Programmierers. Normalerweise kommt eine Funktion zum Einsatz, die über den Elementen iteriert und dabei jedes Element auf Gleichheit mit dem gesuchten überprüft. Wurde das Element gefunden, wird die Iteration abgebrochen.

    In Python existiert für diesen häufigen Fall der in-Operator. Der Operator überprüft, ob die Kollektion das gesuchte Element enthält, und liefert einen entsprechenden booleschen Wert zurück:

    'a' in 'Python'
    'y' in 'Python'

    Mit der list()-Funktion aus einem Iterable eine Liste erzeugen

    Anders als in vielen anderen Sprachen ist es unnötig, eine For-Schleife in Python einzusetzen, um die Buchstaben eines Strings einzeln in eine Liste zu schreiben. Stattdessen nutzen wir die list()-Funktion, um ein Iterable in eine Liste von Elementen zu konvertieren. Schauen wir uns beide Ansätze an einem Beispiel an. Wir iterieren über den Buchstaben eines Wortes und fügen diese einer leeren Liste hinzu:

    word = 'Python'
    letters = []
    for letter in word:
        letters.append(letter)

    Den Aufwand können wir uns sparen. Wir erzeugen die Liste mit der list()-Funktion direkt. Im selben Zuge überprüfen wir mit der Assert-Anweisung, dass beide Methoden dasselbe Resultat liefern:

    assert list(word) == letters

    Ein weiteres Beispiel: Wir erzeugen die Liste der Zahlen null bis neun. Als Basis dient ein Range-Objekt als Iterable:

    list(range(10))

    Neben Listen lassen sich auch Mengen, auf Englisch „Sets“, aus einem Iterable erzeugen. Beispielshalber erzeugen wir ein Set, das die in einem Satz enthaltenen Buchstaben widerspiegelt. Im Anschluss überprüfen wir mit dem in-Operator, dass die Menge der Buchstaben kein 'a' enthält:

    alphabet = set('Python is not hyped')
    assert 'a' not in alphabet

    For-Loops in Python mit Comprehensions ersetzen

    Ein häufiger Nutzen von For-Schleifen in Python besteht darin, die Elemente einer Kollektion zu modifizieren. Ggf. möchten wir basierend auf einer Kollektion neue Werte errechnen oder bestimmte Elemente nach einem Muster filtern. Dem imperativen Programmierstil folgend beschreiben wir die einzelnen Schritte:

    1. Mit For-Schleife über der Kollektion iterieren.
    2. Jedes Element verarbeiten.
    3. Dabei ggf. Untermenge der Elemente zu neuer Kollektion zusammenfassen.

    Für simple Modifikationen ist dies recht aufwendig. Funktionale Sprachen machen es vor, dass es auch einfacher geht. Glücklicherweise kennt Python das Konzept der „Comprehensions“. Comprehensions können simple Anwendungen der For-Schleife in Python ersetzen. Sie sind dabei performanter als äquivalente Anwendungen einer For-Schleife.

    Ein Comprehension erzeugt eine ggf. modifizierte Kollektion basierend auf einem Iterable. Dabei kommt eine knappe, ausdrucksstarke Syntax zum Einsatz. Betrachten wir die generelle Syntax einer List Comprehension. Wir schreiben den Ausdruck zwischen eckige Klammern. Eine Operation wird auf den Elementen einer Kollektion ausgeführt; jedes Element wird dabei in eine neue Liste kopiert:

    [ operation(element) for element in collection ]

    Ferner lassen sich Elemente nach bestimmten Mustern filtern. Zum Einsatz kommt ein optionales if und eine Bedingung:

    [ operation(element) for element in collection if condition(element) ]

    Betrachten wir im Anschluss ein Beispiel für eine For-Schleife in Python, die sich durch eine Comprehension ersetzen lässt. Wir haben eine Liste mit Zahlen und möchten die dazu korrespondierende Liste an Quadratzahlen berechnen:

    numbers = [2, 3, 5, 9, 17]

    Wir erzeugen eine leere Liste und befüllen diese innerhalb einer For-Schleife mit den Quadraten:

    squares = []
    for number in numbers:
        squares.append(number ** 2)

    Einfacher ausdrücken lässt sich die Liste mit Quadratzahlen als Comprehension:

    squares_comp = [number ** 2 for number in numbers]

    Im Anschluss stellen wir mithilfe der Assert-Anweisung sicher, dass beide Methoden dasselbe Ergebnis liefern:

    assert squares == squares_comp

    Ein weiteres Beispiel: Wir möchten Kleinbuchstaben aus einem String extrahieren. Als Eingabe erzeugen wir eine Liste von Buchstaben mit gemischter Groß- und Kleinschreibung:

    word = list("PyThoN")

    Der herkömmliche Weg, die Kleinbuchstaben zu extrahieren, besteht darin, über den Buchstaben zu iterieren. Wir testen jeden Buchstaben mit der islower()-Funktion und fügen ihn bei positivem Resultat zu einer anfangs leeren Liste hinzu:

    lowers = []
    for letter in word:
        if letter.islower():
            lowers.append(letter)

    Diese For-Schleife können wir uns in Python sparen. Wir benutzen stattdessen eine Comprehension, die nur Kleinbuchstaben aus der ursprünglichen Liste kopiert:

    lowers_comp = [ letter for letter in word if letter.islower() ]

    Wiederum überprüfen wir die Gleichheit der beiden Methoden mittels Assert-Anweisung:

    assert lowers == lowers_comp