Generatoren/Iteratoren

Generatoren sind Funktionen die Iteratoren erzeugen.

def generate_gerade_zahlen(start, count):
    # Die erste Zahl auf gerade einstellen
    start = start if start % 2 == 0 else start + 1

    while count > 0:
        yield start
        start += 2
        count -= 1
generate_gerade_zahlen(101, 20)
<generator object generate_gerade_zahlen at 0x10575ba98>
x = generate_gerade_zahlen(101, 20)
print(x)
<generator object generate_gerade_zahlen at 0x10575ba20>
it = generate_gerade_zahlen(101, 20)
for num in it:
    print(num)
102
104
106
108
110
112
114
116
118
120
122
124
126
128
130
132
134
136
138
140
it = generate_gerade_zahlen(101, 6)
print(type(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))
<class 'generator'>
102
104
106
108
110
112
iter(it)
<generator object generate_gerade_zahlen at 0x10575b8b8>
def generate_gerade_zahlen(start, count):
    print("In der Generator-Funktion")

    # Make sure that the first number is even.
    start = start if start % 2 == 0 else start + 1

    while count > 0:
        print(f"[count:{count}] Hallo! vor dem erste yield ....")
        yield start
        print(f"[count:{count}] Ok! Nochmal mal...")
        start += 2
        count -= 1
    print(f"[count:{count}] das war's, mehr geht nicht...")
it = generate_gerade_zahlen(3, 2)
for num in it:
     print(f"Gerade Zahlen: {num}")
In der Generator-Funktion
[count:2] Hallo! vor dem erste yield ....
Gerade Zahlen: 4
[count:2] Ok! Nochmal mal...
[count:1] Hallo! vor dem erste yield ....
Gerade Zahlen: 6
[count:1] Ok! Nochmal mal...
[count:0] das war's, mehr geht nicht...

lazy evaluation

  • Verzögerte Ausführung, ein Iterator wird erzeugt, aber noch nicht ausgeführt.
  • Erst mit dem Aufruf durch next() wird der Zähler weiter gestellt.
  • Nur ein Wert wird im Speicher gehalten.
  • StopIteration Exception wird erzeugt, wenn der Iterator keine Werte mehr generieren kann.
for i in [1,2,3]:
    print(i)
1
2
3
def generate_ints():
    yield 1
    yield 2
    yield 3
for i in generate_ints():
    print(i)
1
2
3

Eigener Iterator

Zwei Regeln:

  • muss _ next _ implementieren
  • wenn mit iter() aufgerufen wird, wird self zurückgegeben
class Range:
  def __init__(self, start, stop, step):
    self.next = start
    self.stop = stop
    self.step = step

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

  def __iter__(self):
    return self
for num in Range(1, 20, 2):
    print(num)
1
3
5
7
9
11
13
15
17
19

Lottozahlen

import random

def lottery():
    "Sechs Zufallszahlen am Stück."
    for i in range(6):
        yield random.randint(1, 49)

for random_number in lottery():
       print(f"Gewinnzahl ist ... {random_number}")
Gewinnzahl ist ... 7
Gewinnzahl ist ... 44
Gewinnzahl ist ... 20
Gewinnzahl ist ... 40
Gewinnzahl ist ... 21
Gewinnzahl ist ... 23
def counter1(n=0):
    """Generator that counts upward forever."""
    while True:
        yield n
        n += 1
class counter2:
    """Iterator that counts upward forever."""
    def __init__(self, n=0):
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        n = self.n
        self.n += 1
        return n
for i in range(1,5):
    print(next(counter1(i)))
1
2
3
4
print(next(counter1(6)))
6