W3docs

Python Iteratoren

Lerne, wie Python-Iteratoren funktionieren, eigene Iterator-Klassen erstellt werden und wann Iteratoren sinnvoller als Listen sind.

Ein Iterator ist eine der grundlegendsten Abstraktionen in Python. Immer wenn du eine for-Schleife schreibst, zip() aufrufst oder einen List Comprehension verwendest, verlässt sich Python im Hintergrund stillschweigend auf das Iterator-Protokoll. Dieses Kapitel erklärt, was Iteratoren sind, wie du eigene erstellst, wie du die umfangreichen eingebauten Iteratoren nutzt und wann Iteratoren das richtige Werkzeug sind.

Was ist ein Iterator?

Python unterscheidet zwischen zwei verwandten Konzepten:

  • Ein Iterable ist jedes Objekt, über das du iterieren kannst — eine list, tuple, str, dict, set oder ein Objekt, dessen Klasse __iter__ definiert. Es kann einen Iterator erzeugen, verfolgt aber nicht selbst die Position.
  • Ein Iterator ist ein Objekt, das den Durchlaufzustand verfolgt. Es implementiert zwei Methoden, die zusammen das Iterator-Protokoll bilden:
    • __iter__() — gibt das Iterator-Objekt selbst zurück. Das ermöglicht es Iteratoren, in for-Schleifen und anderen Iterationskontexten zu funktionieren.
    • __next__() — gibt beim jeden Aufruf den nächsten Wert zurück. Wenn keine Werte mehr vorhanden sind, wird StopIteration ausgelöst.

Der wesentliche Unterschied: Du kannst über eine Liste so oft iterieren, wie du möchtest, weil jede for-Schleife einen neuen Iterator anfordert. Ein Iterator ist einweg und einmalig — sobald er erschöpft ist, löst ein Aufruf von next() stets StopIteration aus.

python— editable, runs on the server
graph LR
  A[Iterator Object] --> B[__iter__]
  B --> C[Returns self]
  A --> D[__next__]
  D --> E[Next Value]
  D --> F{No values left?}
  F -->|Yes| G[Raises StopIteration]
  F -->|No| E

Wie eine for-Schleife Iteratoren verwendet

Die for-Schleife ist nur syntaktischer Zucker für das Iterator-Protokoll. Intern übersetzt Python Folgendes:

for item in some_iterable:
    print(item)

in ungefähr das hier:

_it = iter(some_iterable)   # call __iter__()
while True:
    try:
        item = next(_it)    # call __next__()
    except StopIteration:
        break
    print(item)

Das Verständnis dieser Übersetzung macht deutlich, warum jedes Objekt, das __iter__ und __next__ implementiert, nahtlos in einer for-Schleife, mit zip(), enumerate() und jedem anderen Kontext, der ein Iterable erwartet, funktioniert.

Einen eigenen Iterator erstellen

Um einen eigenen Iterator zu erstellen, definiere eine Klasse, die sowohl __iter__ als auch __next__ implementiert. Hier ist ein Countdown-Iterator, der von einer gegebenen Zahl bis 1 herunterzählt:

class Countdown:
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        return self        # the iterator is its own iterable

    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value

for n in Countdown(5):
    print(n)
# Output:
# 5
# 4
# 3
# 2
# 1

Beachte, dass __iter__ self zurückgibt. Das ermöglicht es, dasselbe Objekt direkt in einer for-Schleife zu platzieren — die Schleife ruft iter() darauf auf, was __iter__() aufruft, das wiederum den Iterator selbst zurückgibt.

Einen Schritt-Parameter hinzufügen

Du kannst innerhalb von __next__ jede beliebige Logik hinzufügen. Hier ist ein StepRange-Iterator, der range() nachahmt, aber einen Schrittwert akzeptiert:

class StepRange:
    def __init__(self, start, stop, step=1):
        self.current = start
        self.stop = stop
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.stop:
            raise StopIteration
        value = self.current
        self.current += self.step
        return value

print(list(StepRange(0, 10, 3)))
# Output: [0, 3, 6, 9]

Der Aufruf von list() auf einem Iterator erschöpft ihn und sammelt alle Werte in einer Liste — ein nützliches Muster, wenn du alle Ergebnisse auf einmal benötigst.

Die eingebauten Funktionen iter() und next()

Die eingebauten Funktionen iter() und next() sind der Standardweg, um direkt mit dem Iterator-Protokoll zu arbeiten.

  • iter(obj) — ruft obj.__iter__() auf und gibt den resultierenden Iterator zurück.
  • next(it) — ruft it.__next__() auf und gibt den nächsten Wert zurück.
  • next(it, default) — gibt default zurück, anstatt StopIteration auszulösen, wenn der Iterator erschöpft ist. Das ist die sicherste Methode, einen Blick auf das nächste Element zu werfen, ohne einen try/except-Block zu verwenden.
words = ["hello", "world"]
it = iter(words)

print(next(it))           # hello
print(next(it))           # world
print(next(it, "done"))   # done  (exhausted; returns default)

Die Zwei-Argument-Form von next() ist besonders nützlich in Streaming- oder Parsing-Szenarien, in denen du das Ende der Eingabe elegant behandeln möchtest.

Iteratoren sind einmalig verwendbar

Das ist der häufigste Fallstrick bei Iteratoren: Sobald ein Iterator erschöpft ist, kann er nicht zurückgespult werden.

it = iter([1, 2, 3])

for x in it:
    print(x)        # prints 1, 2, 3

for x in it:
    print(x)        # prints nothing — iterator is exhausted

Wenn du mehrfach iterieren musst, behalte das ursprüngliche Iterable (z. B. die Liste) und rufe iter() erneut auf, oder verwende einen List Comprehension, um alle Werte zuerst zu materialisieren.

Eingebaute Funktionen, die Iteratoren zurückgeben

Die Standardbibliothek von Python ist auf Iteratoren aufgebaut. Diese Funktionen geben alle Iteratoren statt Listen zurück, sind also auch bei sehr großen Sequenzen speichereffizient:

range()

range(start, stop, step) gibt einen Iterator von ganzen Zahlen zurück. Die ganzen Zahlen werden nicht im Speicher abgelegt — jede wird bei Bedarf berechnet.

for i in range(1, 6):
    print(i)
# Output: 1  2  3  4  5

zip()

zip() nimmt mehrere Iterables und gibt einen Iterator von Tupeln zurück, der Elemente positionsweise zusammenführt. Die Iteration endet beim kürzesten Eingabe-Iterable.

names = ["Alice", "Bob", "Carol"]
scores = [95, 88, 72]

for name, score in zip(names, scores):
    print(f"{name}: {score}")
# Output:
# Alice: 95
# Bob: 88
# Carol: 72

enumerate()

enumerate() umhüllt ein beliebiges Iterable und gibt (Index, Wert)-Paare zurück. Verwende es, um eine manuelle Zählervariable zu vermeiden.

fruits = ["apple", "banana", "cherry"]

for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")
# Output:
# 1. apple
# 2. banana
# 3. cherry

map() und filter()

Beide Funktionen geben Iteratoren zurück (in Python 3). map(fn, iterable) wendet eine Funktion auf jedes Element an; filter(fn, iterable) behält nur Elemente, für die die Funktion True zurückgibt.

numbers = [1, 2, 3, 4, 5]

doubled = list(map(lambda x: x * 2, numbers))
print(doubled)          # [2, 4, 6, 8, 10]

evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)            # [2, 4]

Prüfen, ob ein Objekt ein Iterator ist

Verwende isinstance() mit den abstrakten Basisklassen aus dem Modul collections.abc, um auf Iterierbarkeit und Iterator-Status zu prüfen:

from collections.abc import Iterable, Iterator

my_list = [1, 2, 3]
my_iter = iter(my_list)

print(isinstance(my_list, Iterable))   # True  — list is iterable
print(isinstance(my_list, Iterator))   # False — list is NOT an iterator
print(isinstance(my_iter, Iterator))   # True  — list_iterator is an iterator
print(isinstance(my_iter, Iterable))   # True  — all iterators are also iterables

Jeder Iterator ist auch ein Iterable (weil __iter__ self zurückgibt), aber nicht jedes Iterable ist ein Iterator.

Wann Iteratoren statt Listen verwenden

SituationVerwende
Zufälliger Zugriff erforderlich (items[5])list
Einmaliges Iterieren, Speicher ist wichtigIterator / Generator
Unendliche oder sehr große SequenzenIterator / Generator
Mehrfaches Iterieren erforderlichlist (Original behalten)
Pipeline von Transformationenverkettete Iteratoren (map, filter, itertools)

Bei großen Datensätzen — Millionen von Zeilen aus einer Datei lesen, Streaming-Daten verarbeiten — vermeidet ein Iterator das Laden aller Daten auf einmal in den Speicher. Bei kleinen, endlichen Sammlungen, auf deren Elemente du wiederholt zugreifst, ist eine Liste einfacher.

Iteratoren vs. Generatoren

Ein Generator ist eine komfortable Abkürzung zum Schreiben eines Iterators. Statt einer Klasse mit __iter__ und __next__ schreibst du eine Funktion, die yield verwendet. Python wandelt sie automatisch in einen Iterator um.

# Iterator class
class Countdown:
    def __init__(self, start):
        self.current = start
    def __iter__(self):
        return self
    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value

# Equivalent generator function
def countdown(start):
    while start > 0:
        yield start
        start -= 1

print(list(countdown(5)))   # [5, 4, 3, 2, 1]

Verwende einen klassenbasierten Iterator, wenn du zusätzliche Methoden oder veränderbaren Zustand benötigst, der über das hinausgeht, was ein einfacher Generator bietet. Verwende einen Generator in den meisten anderen Fällen — er ist prägnanter und gleich leistungsfähig.

Weitere Informationen zu yield, Generator-Ausdrücken und send() findest du im Kapitel Python Generatoren.

Übung

Übung
Which methods make up the Python iterator protocol?
Which methods make up the Python iterator protocol?
Was this page helpful?