Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung Nächste Überarbeitung | Vorherige Überarbeitung |
talit:generators [2024-05-17 13:31] – hof | talit:generators [2024-05-22 18:20] (aktuell) – hof |
---|
# Iteratoren und Generatoren in Python | # Iteratoren und Generatoren in Python |
| |
| <html><script type="module" src="https://bottom.ch/ksr/ed/bottom-editor.js"></script></html> |
| |
Eine [[wpde>Folge_(Mathematik)|Folge]] ist in der Mathematik _eine Auflistung von endlich oder unendlich vielen fortlaufend nummerierten Objekten (beispielsweise Zahlen)_. Nur, wie gehen wir praktisch in Python mit dieser Unendlichkeit um? Und könnte uns das Prinzip auch anderweitig weiterhelfen? | Eine [[wpde>Folge_(Mathematik)|Folge]] ist in der Mathematik _eine Auflistung von endlich oder unendlich vielen fortlaufend nummerierten Objekten (beispielsweise Zahlen)_. Nur, wie gehen wir praktisch in Python mit dieser Unendlichkeit um? Und könnte uns das Prinzip auch anderweitig weiterhelfen? |
| |
| |
Wir möchten wiederverwendbaren Code schreiben, der die [[wpde>Fibonacci-Folge]] generiert. Zur Erinnerung: die Fibonacci-Folge beginnt mit den Werten 1 und 1 (oder 0 und 1), der nächste Wert ergibt sich immer aus der Summe der beiden vorherigen. | Wir möchten wiederverwendbaren Code schreiben, der die [[wpde>Fibonacci-Folge]] generiert. Zur Erinnerung: die Fibonacci-Folge beginnt mit den Werten 1 und 1 (oder 0 und 1), der nächste Wert ergibt sich immer aus der Summe der beiden vorherigen. |
| |
### Aufgabe 1: Fibonacci-Liste | ### Aufgabe 1: Fibonacci-Liste |
| |
Unser erster Versuch: Schreibe eine Funktion, die die ersten `n` Elemente der Fibonacci-Folge generiert und in einer Liste zurückgibt. Untenstehender Code funktioniert nur für $n\le10$ -- ändere den Code so, dass er für beliebig grosse $n$ das richtige Resultat zurückgibt! | Unser erster Versuch: Schreibe eine Funktion, die die ersten `n` Elemente der Fibonacci-Folge generiert und in einer Liste zurückgibt. Untenstehender Code funktioniert nur für $n\le10$ -- ändere den Code so, dass er für beliebig grosse $n$ das richtige Resultat zurückgibt! |
| |
<html><iframe frameborder="0" width="100%" style="height: 15lh;" allow="clipboard-write" src="https://tkilla77.github.io/python_editor_wasm/embed.html?code=def+fibonacci%28n%3D10%29%3A%0A++++first_few+%3D+%5B1%2C+1%2C+2%2C+3%2C+5%2C+8%2C+13%2C+21%2C+34%2C+55%5D%0A++++result+%3D+%5B%5D%0A++++for+i+in+range%28n%29%3A%0A++++++++result.append%28first_few%5Bi%5D%29%0A++++return+result%0A%0Aprint%28fibonacci%2810%29%29 | <html><bottom-editor>def fibonacci(n=10): |
"></iframe></html> | first_few = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] |
| result = [] |
| for i in range(n): |
| result.append(first_few[i]) |
| return result |
| |
| print(fibonacci(10))</bottom-editor></html> |
| |
<nodisp 0> | <nodisp 0> |
Analysiere den untenstehenden Code und führe ihn aus. Ändere den Code, so dass die erste Fibonacci Zahl ausgegeben wird, die grösser als 1000 ist! | Analysiere den untenstehenden Code und führe ihn aus. Ändere den Code, so dass die erste Fibonacci Zahl ausgegeben wird, die grösser als 1000 ist! |
| |
<html><iframe frameborder="0" width="100%" style="height: 25lh;" allow="clipboard-write" src="https://tkilla77.github.io/python_editor_wasm/embed.html?code=class+FiboIterator%3A%0A++++def+__init__%28self%2C+limit+%3D+-1%29%3A%0A++++++++%23+Constructor+-+hier+wird+der+Initial-Zustand+gespeichert%0A++++++++self.one+%3D+0%0A++++++++self.two+%3D+1%0A++++++++self.limit+%3D+limit%0A++++%0A++++def+__next__%28self%29%3A%0A++++++++if+self.limit+%3D%3D+0%3A%0A++++++++++++raise+StopIteration%28%29%0A++++++++self.one%2C+self.two+%3D+self.two%2C+self.one+%2B+self.two%0A++++++++self.limit+-%3D+1%0A++++++++return+self.one%0A%0Aiterator+%3D+FiboIterator%28%29%0Aprint%28next%28iterator%29%29%0Aprint%28next%28iterator%29%29%0Aprint%28next%28iterator%29%29"></iframe></html> | <html><bottom-editor>class FiboIterator: |
| def __init__(self, limit = -1): |
| # Constructor - hier wird der Initial-Zustand gespeichert |
| self.one = 0 |
| self.two = 1 |
| self.limit = limit |
| |
| def __next__(self): |
| if self.limit == 0: |
| raise StopIteration() |
| self.one, self.two = self.two, self.one + self.two |
| self.limit -= 1 |
| return self.one |
| |
| iterator = FiboIterator() |
| print(next(iterator)) |
| print(next(iterator)) |
| print(next(iterator))</bottom-editor></html> |
| |
<nodisp 0> | <nodisp 0> |
++++ | ++++ |
</nodisp> | </nodisp> |
| |
| |
## Iterables | ## Iterables |
Untenstehender Code fügt dem bestehenden Iterator ein Iterable hinzu, so dass wir eine for-Schleife nützen können: | Untenstehender Code fügt dem bestehenden Iterator ein Iterable hinzu, so dass wir eine for-Schleife nützen können: |
| |
<html><iframe frameborder="0" width="100%" style="height: 25lh;" allow="clipboard-write" src="https://tkilla77.github.io/python_editor_wasm/embed.html?code=class+FiboIterator%3A%0A++++def+__init__%28self%2C+limit+%3D+-1%29%3A%0A++++++++%23+Hier+wird+der+Zustand+gespeichert%0A++++++++self.one+%3D+0%0A++++++++self.two+%3D+1%0A++++++++self.limit+%3D+limit%0A++++%0A++++def+__next__%28self%29%3A%0A++++++++if+self.limit+%3D%3D+0%3A%0A++++++++++++raise+StopIteration%28%29%0A++++++++self.one%2C+self.two+%3D+self.two%2C+self.one+%2B+self.two%0A++++++++self.limit+-%3D+1%0A++++++++return+self.one%0A%0Aclass+FiboIterable%3A%0A++++%23+Wird+einmal+aufgerufen%2C+wenn+Python+den+die+for-Schleife+startet.%0A++++def+__iter__%28self%29%3A%0A++++++++return+FiboIterator%28%29%0A++++%0Asequence+%3D+FiboIterable%28%29%0Afor+num+in+sequence%3A%0A++++if+num+%3E+1000%3A%0A++++++++print%28f%27Erste+Fibonacci-Zahl+gr%C3%B6sser+als+1000+ist+%7Bnum%7D%27%29%0A++++++++break%0A | <html><bottom-editor>class FiboIterator: |
"></iframe></html> | def __init__(self, limit = -1): |
| # Hier wird der Zustand gespeichert |
| self.one = 0 |
| self.two = 1 |
| self.limit = limit |
| |
| def __next__(self): |
| if self.limit == 0: |
| raise StopIteration() |
| self.one, self.two = self.two, self.one + self.two |
| self.limit -= 1 |
| return self.one |
| |
| class FiboIterable: |
| # Wird einmal aufgerufen, wenn Python die for-Schleife startet. |
| def __iter__(self): |
| return FiboIterator() |
| |
| sequence = FiboIterable() |
| for num in sequence: |
| if num > 1000: |
| print(f'Erste Fibonacci-Zahl grösser als 1000 ist {num}') |
| break |
| </bottom-editor></html> |
| |
Die Funktionen `__iter__` und `__next__` dürfen auch von derselben Klasse implementiert werden. | Die Funktionen `__iter__` und `__next__` dürfen auch von derselben Klasse implementiert werden. |
Ein Generator ist ein Iterable und kann damit auch als Ausdruck nach `in` in einer `for ... in <expression>` stehen. | Ein Generator ist ein Iterable und kann damit auch als Ausdruck nach `in` in einer `for ... in <expression>` stehen. |
| |
<html><iframe frameborder="0" width="100%" style="height: 15lh;" allow="clipboard-write" src="https://tkilla77.github.io/python_editor_wasm/embed.html?code=def+fibo%28%29%3A%0A++++one+%3D+0%0A++++two+%3D+1%0A++++while+True%3A%0A++++++++one%2C+two+%3D+two%2C+one+%2B+two%0A++++++++yield+one%0A%0Afor+num+in+fibo%28%29%3A%0A++++if+num+%3E+1000%3A%0A++++++++print%28f%27Erste+Fibonacci-Zahl+gr%C3%B6sser+als+1000+ist+%7Bnum%7D%27%29%0A++++++++break%0A | <html><bottom-editor>def fibo(): |
"></iframe></html> | one = 0 |
| two = 1 |
| while True: |
| one, two = two, one + two |
| yield one |
| |
| for num in fibo(): |
| if num > 1000: |
| print(f'Erste Fibonacci-Zahl grösser als 1000 ist {num}') |
| break |
| </bottom-editor></html> |
| |
### Iteration beenden | ### Iteration beenden |
| |
Sind keine Elemente mehr übrig, wird einfach nichts zurückgegeben bzw. ein normales `return`. | Sind keine Elemente mehr übrig, wird einfach nichts zurückgegeben bzw. ein normales `return`. |
| ### Aufgabe 3: Limitieren |
#### Aufgabe 3: Limitieren | |
| |
Verändere den obigen Code um ein optionales Limit einzubauen, wieviele Elemente erzeugt werden sollen. | Verändere den obigen Code um ein optionales Limit einzubauen, wieviele Elemente erzeugt werden sollen. |
Der folgende Code soll also `143` ausgeben: | Der folgende Code soll also `143` ausgeben: |
| |
<html><iframe frameborder="0" width="100%" style="height: 15lh;" allow="clipboard-write" src="https://tkilla77.github.io/python_editor_wasm/embed.html?code=def+fibo%28limit%3D-1%29%3A%0A++++pass%0A%0An%3D10%0Aprint%28f%27Summe+der+ersten+%7Bn%7D+Fibonacci-Zahlen%3A+%7Bsum%28fibo%28n%29%29%7D%27%29 | <html><bottom-editor>def fibo(limit=-1): |
"></iframe></html> | pass |
| |
| n=10 |
| print(f'Summe der ersten {n} Fibonacci-Zahlen: {sum(fibo(n))}') |
| </bottom-editor></html> |
| |
## Yield From | ## Yield From |
Beispiel: ein einfacher Binärbaum besteht aus Knoten (_en_: Nodes), die jeweils bis zu zwei Kinder haben können. | Beispiel: ein einfacher Binärbaum besteht aus Knoten (_en_: Nodes), die jeweils bis zu zwei Kinder haben können. |
| |
{{:talit:generators:pasted:20240506-131941.png}} | {{:talit:generators:pasted:20240506-131941.png?nolink&400}} |
| |
Um den Baum zu traversieren (d.h. alle Knoten zu besuchen), können wir zum Beispiel die [[wpde>Binärbaum#Traversierung|In-Order Traversierung]] wählen: Zuerst wird der linke Teilbaum durchlaufen, anschliessend der Knoten selbst, zum Schluss der rechte Teilbaum. | Um den Baum zu traversieren (d.h. alle Knoten zu besuchen), können wir zum Beispiel die [[wpde>Binärbaum#Traversierung|In-Order Traversierung]] wählen: Zuerst wird der linke Teilbaum durchlaufen, anschliessend der Knoten selbst, zum Schluss der rechte Teilbaum. |
Eine rekursive Umsetzung könnte wie folgt aussehen - beachte, wie `yield from` eingesetzt wird, um die rekursive Traversierung zu erreichen. | Eine rekursive Umsetzung könnte wie folgt aussehen - beachte, wie `yield from` eingesetzt wird, um die rekursive Traversierung zu erreichen. |
| |
<html><iframe frameborder="0" width="100%" style="height: 25lh;" allow="clipboard-write" src="https://tkilla77.github.io/python_editor_wasm/embed.html?code=class+Node%3A%0A++++def+__init__%28self%2C+key%2C+left%3DNone%2C+right%3DNone%29%3A%0A++++++++self.key+%3D+key%0A++++++++self.left+%3D+left%0A++++++++self.right+%3D+right%0A++++%0A++++def+in_order_traversal%28self%29%3A%0A++++++++if+self.left%3A%0A++++++++++++yield+from+self.left.in_order_traversal%28%29%0A++++++++yield+self%0A++++++++if+self.right%3A%0A++++++++++++yield+from+self.right.in_order_traversal%28%29%0A%0A%0Atree+%3D+Node%285%2C+Node%281%29%2C+Node%287%29%29%0A%0Afor+node+in+tree.in_order_traversal%28%29%3A+print+%28node.key%29 | <html><bottom-editor>class Node: |
"></iframe></html> | def __init__(self, key, left=None, right=None): |
| self.key = key |
| self.left = left |
| self.right = right |
| |
| def in_order_traversal(self): |
| if self.left: |
| yield from self.left.in_order_traversal() |
| yield self |
| if self.right: |
| yield from self.right.in_order_traversal() |
| |
| |
| tree = Node(5, Node(1), Node(7)) |
| |
| for node in tree.in_order_traversal(): print (node.key) |
| </bottom-editor></html> |
| |
### Aufgabe 4: Pre-Order Traversierung | ### Aufgabe 4: Pre-Order Traversierung |