Iteratoren

Grundlegendes

Die meisten Containerobjekte können mit einer for-Schleife angeprochen werden:

In [2]:
for element in [1, 2, 3]:
    print(element)
for element in (1, 2, 3):
    print(element)
for key in {'one':1, 'two':2}:
    print(key)
for char in "123":
    print(char)
for line in open("myfile.txt"):
    print(line, end='')
1
2
3
1
2
3
one
two
1
2
3
Dies ist ein Testfile
mit Text in
mehreren Zeilen

Diese Art von Zugriff auf Containerobjekte ist klar, prägnant und bequem und beruht auf der Verwendung von Iteratoren. Die Verwendung von Iteratoren durchdringt und vereinheitlicht Python und ist eine wichtige Grundlage für das Schreiben von funktionalen Programmen.

Ein Iterator ist ein Objekt, das einen Datenstrom darstellt; Dieses Objekt gibt die Daten jeweils als ein Element nach dem anderen zurück.

Algorithmik

Hinter den Kulissen ruft die for-Schleife die Builtin-Funktion iter() auf das entsprechende Containerobjekt auf. Diese Funktion gibt ein Iteratorobjekt zurück, in dem die Methode __next__() definiert sein muss und die jeweils ein Element nach dem anderen aus dem Container zurückliefert. Die Methode __next__() darf auch keine Argumente annehmen. Wenn es keine Elemente mehr gibt, wirft __next__() eine StopIteration-Ausnahme, welche die for-Schleife beendet. Die Methode __next__() kann mit Builtin-Funktion next() aufgerufen werden; Das folgende Beispiel zeigt, wie alles funktioniert:

In [3]:
s = 'abc'
it = iter(s)
In [4]:
it
Out[4]:
<str_iterator at 0x7fccc35b1ac8>
In [5]:
next(it)
Out[5]:
'a'
In [6]:
next(it)
Out[6]:
'b'
In [7]:
next(it)
Out[7]:
'c'
In [8]:
next(it)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-8-2cdb14c0d4d6> in <module>()
----> 1 next(it)

StopIteration: 

Hier ein weiteres Beispiel, diesmal mit einer Liste anstelle einer Zeichenkette:

In [9]:
L = [1,2,3]
it = iter(L)
it  
Out[9]:
<list_iterator at 0x7fcce43acbe0>
In [10]:
it.__next__()  # identisch mit next(it)
Out[10]:
1
In [11]:
next(it)
Out[11]:
2
In [12]:
next(it)
Out[12]:
3
In [13]:
next(it)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-13-2cdb14c0d4d6> in <module>()
----> 1 next(it)

StopIteration: 

Allgemein gesprochen, nimmt die Builtin-Funktion iter() ein beliebiges Objekt und versucht, einen Iterator zurückzugeben. Der Iterator gibt dann über die Methode __next__() den Inhalt bzw. die Elemente des Objekts Schritt für Schritt zurück. Wenn das entsprechende Objekt keine Iteration unterstützt, wird ein TypeError geworfen. Mehrere der in Python integrierten Datentypen unterstützen die Iteration, die häufigsten sind Listen und Dictionaries. Ein Objekt heißt iterabel, wenn es einen Iterator dafür gibt.

Einbinden eines Iterators in eine Klasse

Nachdem nun die Algorithmik hinter dem Iterator-Protokoll bekannt ist, ist es einfach, das Iterator-Verhalten in eine Klasse einzubinden. Es muss eine __iter__()-Methode definiert werden, die ein Objekt mit einer __next__()-Methode zurückgibt. Wenn in der Klasse die Methode __next__() definiert ist, dann kann die Methode __iter__() einfach self zurückgeben:

In [14]:
class Reverse:
    """Iterator um eine Sequenz rückwärts zu durchlaufen"""
    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]
In [15]:
rev = Reverse('spam')
iter(rev)
Out[15]:
<__main__.Reverse at 0x7fccc356eba8>
In [16]:
for char in rev:
    print(char)
m
a
p
s
In [17]:
%%Mooc MoocStringAssessment
Out[17]:

Iterator Ausnahme

Wie lautet die Ausnahme, die geworfen wird, wenn das Ende eines Iterator-Durchlaufs erreicht ist ?



Nutzung von Iteratoren

Python erwartet iterable Objekte in verschiedenen Kontexten, die wichtigste ist die - wie oben bereits angesprochen und eingeführt - for-Schleife. In der Anweisung for X in Y muss Y ein Iterator oder ein Objekt sein, für das die Builtin-Funktion iter() einen Iterator erzeugen kann. Die folgenden beiden Aussagen sind gleichwertig:

for i in iter(obj):
    print(i)

for i in obj:
    print(i)

Die Elemente von Iteratoren sind per se nur über die __next__()-Methode aufrufbar. Sie können jedoch als Listen oder Tupel mithilfe der list() oder tuple() Konstruktorfunktionen materialisiert werden:

In [18]:
L = [1,2,3]
iterator = iter(L)
iterator
Out[18]:
<list_iterator at 0x7fccc3574080>
In [19]:
tuple(iterator)
Out[19]:
(1, 2, 3)

Das Entpacken von Sequenzen (sequence unpacking) unterstützt ebenfalls Iteratoren: Wenn bekannt ist, dass ein Iterator N Elemente zurückgibt, können dieser in ein N-Tupel ausgepackt werden:

In [20]:
L = [1,2,3]
iterator = iter(L)
a,b,c = iterator
a,b,c
Out[20]:
(1, 2, 3)

Nutzung durch Builtin-Funktionen und -Operatoren

Builtin-Funktionen wie max() und min() können ein einziges Iterator-Argument aufnehmen und das größte oder kleinste Element zurückgeben. Die Operatoren in und not in unterstützen ebenfalls Iteratoren: X in iterator ist wahr, wenn X in dem vom Iterator zurückgegebenen Datenstrom gefunden wird.

Unendliche Iteratoren

Iteratoren müssen nicht endlich sein; Es kann vollkommen vernünftig sein, einen Iterator zu schreiben, der einen unendlichen Datenstrom erzeugt. Allerdings kann bei einem unendlichen Iterator offensichtlich ein Problem auftauchen: max() oder min() wird niemals zurückkehren und wenn das Element X nicht im Datenstrom enthalten ist, kehren auch die Operatoren in und not in entsprechend nicht zurück.

Einmalige Nutzung

Zu beachten ist, dass man in einem Iterator nur vorwärts gehen kann. Es gibt keine Möglichkeit, das vorherige Element zu erhalten, den Iterator zurückzusetzen oder eine Kopie davon zu machen. Iterator-Objekte können optional diese zusätzlichen Fähigkeiten bereitstellen, aber das Iterator-Protokoll schreibt nur die Methode __next__() zwingend vor. Funktionen können daher alle Ausgaben des Iterators aufbrauchen, und wenn etwas anderes mit dem gleichen Stream gemacht werden muss, muss ein neuer Iterator erstellt werden.

Weitere Datentypen, die Iteratoren unterstützen

Wir haben schon gesehen, wie Listen und Tupel Iteratoren unterstützen. In der Tat wird jeder Sequenz-Typ in Python, wie z.B. Zeichenketten (Strings), automatisch die Erstellung eines Iterators unterstützen.

Dictionaries

Das Aufrufen von iter() auf ein Dictionary gibt einen Iterator zurück, der über die Schlüsselwörter des Wörterbuchs läuft:

In [21]:
m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
     'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
for key in m:  
    print(key, m[key])
Jan 1
Feb 2
Mar 3
Apr 4
May 5
Jun 6
Jul 7
Aug 8
Sep 9
Oct 10
Nov 11
Dec 12

Zu Beachten ist, dass die Reihenfolge im Wesentlichen zufällig ist, weil sie auf der Reihenfolge des Hash-Algorithmus für die Objekte im Dictionary basiert.

Das Anwenden von iter() auf ein Dictionary läuft immer über die Schlüsselwörter, aber Dictionaries haben Methoden, die andere Iteratoren zurückgeben. Wenn über Werte oder Schlüssel/Wert-Paare iteriert werden soll, dann können die Methoden values() oder items() explizit aufgerufen werden, um einen entsprechenden Iterator zu erhalten.

Der dict()-Konstruktor kann einen Iterator akzeptieren, der einen endlichen Datenstrom von (key,value)-Tupeln zurückgibt:

In [22]:
L = [('Italy', 'Rome'), ('France', 'Paris'), ('US', 'Washington DC')]
dict(iter(L))
Out[22]:
{'France': 'Paris', 'Italy': 'Rome', 'US': 'Washington DC'}

Files also support iteration by calling the readline() method until there are no more lines in the file. This means you can read each line of a file like this:

Dateien

Dateien unterstützen ebenfalls die Iteration, indem die readline()-Methode aufrufen, bis keine weiteren Zeilen mehr in der Datei vorhanden sind. Dies bedeutet, dass eine Datei wie folgt gelesen werden kann:

for line in file:
    # hier kann irgendetwas mit der Zeile gemacht werden
    ...

Mengen

Sets können ihren Inhalt von einem Iterator bekommen und man kann über die Elemente des Sets iterieren:

In [23]:
S = {2, 3, 5, 7, 11, 13}
for i in S:
    print(i)
2
3
5
7
11
13
In [24]:
%%Mooc MoocMultipleChoiceAssessment
Out[24]:

Iteratoren

Es sind die folgenden Objekte gegeben:

  • a=list(...)
  • b=dict(...)
  • c=map(...)
  • d=c in a
  • e=zip(...)
  • f="..."
  • g=set(...)
  • h=open(...)
  • Welche der obigen Objekte liefert keinen Iterator zurück?

    a
    b
    b.keys()
    b.items()
    c
    d
    e
    f
    g
    h

In [25]:
%%Mooc Video
Out[25]:

Weitere Literatur

In [26]:
%%Mooc WebReference

Iterators

https://docs.python.org/3/tutorial/classes.html#iterators

Hinweis: Kurze Einführung in Iteratoren

In [27]:
%%Mooc WebReference

Iterators

https://docs.python.org/3/howto/functional.html#iterators

Hinweis: Iteratoren als Grundlage funktionaler Programmierung