Iteratoren und Iterablen

Unterschied zwischen Iterator und iterierbarem Objekt

Python-Foren und Frage-und-Antwort-Websites wie Quora und Stack Overflow sind voller Fragen zu den Begriffen "Iterator" und "iterierbar". Einige möchten wissen, wie diese Begriffe definiert sind, andere suchen nach einer einfachen Möglichkeit, zu überprüfen, ob ein Objekt ein Iterator oder ein iterierbares Objekt ist. Zu diesem Zweck stellen wir eine Funktion zur Verfügung.

Wir haben gesehen, dass wir verschiedene Python-Objekte wie Listen, Tupel und Strings durchlaufen können. Zum Beispiel:

In [1]:
for stadt in ["Berlin", "Wien", "Zürich"]:
    print(stadt)
for sprache in ("Python", "Perl", "Ruby"):
    print(sprache)
for charakter in "Iteration ist einfach":
    print(charakter)
Berlin
Wien
Zürich
Python
Perl
Ruby
I
t
e
r
a
t
i
o
n
 
i
s
t
 
e
i
n
f
a
c
h

Diese Form der Schleife kann als Iteration angesehen werden. Iteration ist jedoch nicht auf explizite for-Schleifen beschränkt. Wenn Sie beispielsweise die Funktion sum() auf einer Liste von ganzzahligen Werten aufrufen, wird ebenfalls eine Iteration durchgeführt.

Was ist der Unterschied zwischen einem iterierbaren Objekt (Iterable) und einem Iterator?

Iterables und Iteratoren haben Ähnlichkeiten: Über beide können Sie mit einer for-Schleife iterieren. Jeder Iterator ist ein Iterable, aber nicht jedes Iterable ist ein Iterator. Ein Beispiel: Eine Liste ist ein Iterable, aber keine Liste ist von sich aus ein Iterator. Ein Iterator kann jedoch mit der Funktion iter() aus einem Iterable erzeugt werden. Damit dies möglich ist, muss die Klasse des Objekts entweder die Methode __iter__ implementieren, die einen Iterator zurückgibt, oder eine Methode __getitem__, die den sequentiellen Zugriff über Indizes (beginnend bei 0) ermöglicht.

Ein Iterator hingegen ist ein Objekt, das über die Methode __next__ verfügt. Diese Methode wird aufgerufen, wenn Sie die Funktion next() verwenden.

Was passiert hinter den Kulissen, wenn eine for-Schleife ausgeführt wird?

Die for-Schleife ruft zu Beginn iter() für das Objekt auf, über das sie iterieren soll. Dieses Objekt muss ein sogenanntes Containerobjekt (ein Iterable) sein. Wenn der Aufruf von iter() erfolgreich ist, wird ein Iterator-Objekt zurückgegeben, das die Methode __next__ implementiert. Diese Methode greift nacheinander auf die Elemente des Objekts zu. Wenn keine weiteren Elemente mehr vorhanden sind, löst die Methode __next__ eine StopIteration-Ausnahme aus, die das Ende der Iteration signalisiert. Sobald die for-Schleife die StopIteration-Ausnahme abfängt, wird sie beendet.

Sie können die Methode __next__ auch explizit durch die eingebaute Funktion next() aufrufen. So funktioniert es:

In [1]:
städte = ["Berlin", "Wien", "Zürich"]
iterator_obj = iter(städte)
print(iterator_obj)

print(next(iterator_obj))
print(next(iterator_obj))
print(next(iterator_obj))
# StopIteration wird ausgelöst, wenn keine 
# weiteren Elemente vorhanden sind
<list_iterator object at 0x79803a7dfd30>
Berlin
Wien
Zürich

Wenn wir noch einmal next(iterator_obj) aufrufen würden, würde eine StopIteration-Ausnahme ausgelöst.

Die folgende Funktion is_iterable gibt True zurück, wenn das Objekt obj iterierbar ist, andernfalls False:

In [2]:
def is_iterable(obj):
    try:
        iter(obj)
        return True
    except TypeError:
        return False
        
for element in [34, [4, 5], (4, 5), {"a":4}, "dfsdf", 4.5]:
    print(element, "iterable: ", is_iterable(element))
34 iterable:  False
[4, 5] iterable:  True
(4, 5) iterable:  True
{'a': 4} iterable:  True
dfsdf iterable:  True
4.5 iterable:  False

Wir haben beschrieben, wie ein Iterator funktioniert. Wenn Sie Ihrer Klasse also ein Iteratorverhalten hinzufügen möchten, müssen Sie Ihrer Klasse die Methoden __iter__ und __next__ hinzufügen.

  • Die Methode __iter__ gibt ein Iteratorobjekt zurück.
  • Wenn die Klasse eine __next__-Methode enthält, reicht es aus, dass die __iter__-Methode self zurückgibt, d. h. einen Verweis auf sich selbst.

Hier ist ein Beispiel für eine benutzerdefinierte Klasse, die Iteratorverhalten implementiert:

In [3]:
class Reverse:
    """
    Erstellt Iteratoren zum Rückwärtslaufen einer Sequenz.
    """
    
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

lst = [34, 978, 42]
lst_rückwärts = Reverse(lst)
for el in lst_rückwärts:
    print(el)
42
978
34