Zum Inhalt springen
aviral gupta

// I3.4 · ca. 35 Min. · Aufbau

itertools

Nach dieser Lektion verbinden, schneiden und stückeln Sie jedes Iterable mit chain, islice und batched, gruppieren sortierte Daten mit groupby und bilden laufende Summen und Kombinationen mit accumulate und product.

Lektion 4 von 6 in I3 Iteration und funktionale Werkzeuge

Danach können Sie

  • Iterables mit chain, islice und batched verbinden, schneiden und stückeln
  • Sortierte Daten mit groupby gruppieren und erklären, warum nach demselben Schlüssel sortiert sein muss
  • Laufende Ergebnisse mit accumulate und alle Kombinationen mit product bilden
  1. Aufwärmen · Aufgabe 1 von 7

    Aufwärmen aus Lektion I3.3: Was gibt dieser Code aus?

    g = (n * 2 for n in [1, 2, 3])
    print(next(g), sum(g))
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen: groupby auf unsortierten Buchstaben. Was gibt der Code aus?

    from itertools import groupby
    
    for key, group in groupby("AABAA"):
        print(key, len(list(group)), end="; ")
  3. Üben · Aufgabe 3 von 7

    naturals() endet nie. Setzen Sie die itertools-Funktion ein, die die ersten drei Werte nimmt.

    from itertools import islice
    
    def naturals():
        n = 1
        while True:
            yield n
            n += 1
    
    print(list(____(naturals(), 3)))
    print(list((naturals(), 3)))
  4. Üben · Aufgabe 4 von 7

    chain nimmt beliebige Iterables. Was gibt dieser Code aus?

    from itertools import chain
    
    print(list(chain([1, 2], "ab", range(2))))
  5. Üben · Aufgabe 5 von 7

    Ordnen Sie jedem Aufruf die Liste zu, die er ergibt.

  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Die Gruppen werden in einem Dict gespeichert und nach der Schleife gelesen. Was gibt der Code aus?

    from itertools import groupby
    
    groups = {}
    for key, group in groupby("aabbb"):
        groups[key] = group
    print({k: list(g) for k, g in groups.items()})
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe. sales = [("north", 120), ("south", 80), ("north", 50), ("east", 70), ("south", 30)]. Geben Sie mit sorted und groupby die Summe pro Region aus, je eine Zeile in alphabetischer Reihenfolge. Geben Sie dann mit accumulate die laufende Summe aller Verkäufe in der gegebenen Reihenfolge aus.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Der Schichtbericht eines Cafés

Die Bestellungen zweier Schichten werden mit chain verbunden, und islice wirft einen Blick auf die ersten beiden. sorted und groupby zeigen, wer welches Getränk bestellt hat, mit einer Schlüsselfunktion für beide. accumulate liefert die laufende Zahl der Tassen, batched setzt je zwei Gäste an einen Tisch, und product baut die Karte aus Größen und Getränken.

main.py

from itertools import accumulate, batched, chain, groupby, islice, product

morning = [("Ada", "tea"), ("Ben", "coffee"), ("Cy", "tea")]
evening = [("Dee", "coffee"), ("Eve", "tea")]

orders = list(chain(morning, evening))
print("first two:", list(islice(orders, 2)))


def drink(order: tuple[str, str]) -> str:
    return order[1]


for name, group in groupby(sorted(orders, key=drink), key=drink):
    print(name, [who for who, _ in group])

cups = [3, 5, 2, 4]
print("running total:", list(accumulate(cups)))
print("tables:", list(batched(["T1", "T2", "T3", "T4", "T5"], 2)))
sizes = ["small", "large"]
print("menu:", [f"{s} {d}" for s, d in product(sizes, ["tea", "coffee"])])

Ausführen mit

python main.py

Ausgabe

first two: [('Ada', 'tea'), ('Ben', 'coffee')]
coffee ['Ben', 'Dee']
tea ['Ada', 'Cy', 'Eve']
running total: [3, 8, 10, 14]
tables: [('T1', 'T2'), ('T3', 'T4'), ('T5',)]
menu: ['small tea', 'small coffee', 'large tea', 'large coffee']
  • Das Sortieren nach Getränk stellte beide Kaffees und alle drei Tees nebeneinander, also bekam jedes Getränk eine Gruppe.
  • Die List Comprehension las jede Gruppe in der Schleife, solange sie noch sichtbar war.
  • Am letzten Tisch sitzt ein Gast: batched macht das letzte Tupel kürzer, statt es aufzufüllen.
  • product änderte das Getränk am schnellsten, wie eine innere for-Schleife: small tea, small coffee, dann large.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 3

Wörter nach Anfangsbuchstaben

group_by_first_letter(words) liefert ein Dict, das jedem Anfangsbuchstaben die Wörter zuordnet, die mit ihm beginnen, jede Liste alphabetisch. Der Starter verwendet groupby ohne Sortieren und funktioniert darum nur mit schon sortierten Eingaben. Korrigieren Sie ihn, sodass ["bat", "apple", "bee", "axe"] {"a": ["apple", "axe"], "b": ["bat", "bee"]} ergibt.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    groupby verbindet nur Nachbarn, gleiche Anfangsbuchstaben müssen also erst nebeneinanderstehen.

  2. Hinweis 2

    Alphabetisch sortierte Wörter sind auch nach dem Anfangsbuchstaben sortiert, und das ist der groupby-Schlüssel.

  3. Hinweis 3

    Schreiben Sie groupby(sorted(words), key=lambda w: w[0]).

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

from itertools import groupby


def group_by_first_letter(words: list[str]) -> dict[str, list[str]]:
    """Map each first letter to the words that start with it."""
    result: dict[str, list[str]] = {}
    for letter, group in groupby(sorted(words), key=lambda w: w[0]):
        result[letter] = list(group)
    return result


if __name__ == "__main__":
    print(group_by_first_letter(["bat", "apple", "bee", "axe"]))
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

from itertools import groupby


def group_by_first_letter(words: list[str]) -> dict[str, list[str]]:
    """Map each first letter to the words that start with it."""
    result: dict[str, list[str]] = {}
    for letter, group in groupby(words, key=lambda w: w[0]):
        result[letter] = list(group)
    return result


if __name__ == "__main__":
    print(group_by_first_letter(["bat", "apple", "bee", "axe"]))

test_main.py

from main import group_by_first_letter


def test_sorted_input():
    """Sortierte Eingaben werden gruppiert"""
    got = group_by_first_letter(["apple", "axe", "bat"])
    assert got == {"a": ["apple", "axe"], "b": ["bat"]}, f"Ergebnis {got!r}"


def test_unsorted_input():
    """Unsortierte Eingaben ergeben einen Eintrag pro Buchstabe, mit jedem Wort"""
    got = group_by_first_letter(["bat", "apple", "bee", "axe"])
    expected = {"a": ["apple", "axe"], "b": ["bat", "bee"]}
    assert got == expected, f"für ['bat', 'apple', 'bee', 'axe'] kam {got!r}, erwartet: {expected!r}"


def test_empty():
    """Ohne Wörter kommt ein leeres Dict"""
    got = group_by_first_letter([])
    assert got == {}, f"für [] kam {got!r}, erwartet: {{}}"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 2 von 3

Kontostände und Höchstwerte mit accumulate

Schreiben Sie zwei Funktionen mit accumulate. balances(start, changes) liefert den Kontostand vor und nach jeder Änderung: balances(100, [20, -50, 10]) ist [100, 120, 70, 80]. peaks(values) liefert an jeder Position den bisher höchsten Wert: peaks([3, 1, 4, 1, 5]) ist [3, 3, 4, 4, 5]. Beide geben Listen zurück.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    accumulate(changes) allein liefert laufende Summen der Änderungen, ohne den Anfangsstand.

  2. Hinweis 2

    Das Schlüsselwortargument initial=start stellt den Anfangsstand voran und addiert die Änderungen dazu.

  3. Hinweis 3

    Für peaks übergeben Sie max als zweites Argument: accumulate(values, max) behält das Größere aus bisherigem Ergebnis und nächstem Wert.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

from itertools import accumulate


def balances(start: int, changes: list[int]) -> list[int]:
    """Return the balance before and after each change."""
    return list(accumulate(changes, initial=start))


def peaks(values: list[int]) -> list[int]:
    """Return the highest value seen so far, at each position."""
    return list(accumulate(values, max))


if __name__ == "__main__":
    print(balances(100, [20, -50, 10]))
    print(peaks([3, 1, 4, 1, 5]))
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

from itertools import accumulate


def balances(start: int, changes: list[int]) -> list[int]:
    """Return the balance before and after each change."""
    return [start]


def peaks(values: list[int]) -> list[int]:
    """Return the highest value seen so far, at each position."""
    return list(values)


if __name__ == "__main__":
    print(balances(100, [20, -50, 10]))
    print(peaks([3, 1, 4, 1, 5]))

test_main.py

from main import balances, peaks


def test_balances():
    """100 mit den Änderungen 20, -50 und 10 ergibt 100, 120, 70, 80"""
    got = balances(100, [20, -50, 10])
    assert got == [100, 120, 70, 80], f"balances(100, [20, -50, 10]) lieferte {got!r}"


def test_no_changes():
    """Ohne Änderungen bleibt nur der Anfangsstand"""
    got = balances(5, [])
    assert got == [5], f"balances(5, []) lieferte {got!r}, erwartet: [5]"


def test_peaks():
    """peaks([3, 1, 4, 1, 5]) ist [3, 3, 4, 4, 5]"""
    got = peaks([3, 1, 4, 1, 5])
    assert got == [3, 3, 4, 4, 5], f"peaks([3, 1, 4, 1, 5]) lieferte {got!r}"


def test_peaks_empty():
    """peaks([]) ist []"""
    got = peaks([])
    assert got == [], f"peaks([]) lieferte {got!r}, erwartet: []"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 3 von 3

Eine Seite mit Ergebnissen

page(items, size, number) liefert Seite number (ab 1 gezählt) von items als Liste, mit size Elementen pro Seite, oder [], wenn es die Seite nicht gibt. page("abcdefg", 3, 3) ist ["g"]. Es muss mit jedem Iterable funktionieren, auch mit einem Generator. Bilden Sie die Seiten mit batched und springen Sie mit islice zur richtigen.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    batched(items, size) liefert die Seiten als Tupel, eine nach der anderen.

  2. Hinweis 2

    islice(pages, number - 1, None) überspringt die Seiten vor der gesuchten.

  3. Hinweis 3

    next(..., ()) nimmt diese Seite, oder ein leeres Tupel, wenn es keine gibt. Geben Sie sie als Liste zurück.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

from collections.abc import Iterable
from itertools import batched, islice


def page(items: Iterable[str], size: int, number: int) -> list[str]:
    """Return page number (counting from 1), size items per page."""
    batch = next(islice(batched(items, size), number - 1, None), ())
    return list(batch)


if __name__ == "__main__":
    print(page("abcdefg", 3, 2))
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

from collections.abc import Iterable
from itertools import batched, islice


def page(items: Iterable[str], size: int, number: int) -> list[str]:
    """Return page number (counting from 1), size items per page."""
    return list(islice(items, size))


if __name__ == "__main__":
    print(page("abcdefg", 3, 2))

test_main.py

from main import page


def test_first_page():
    """Seite 1 enthält die ersten drei Buchstaben"""
    got = page("abcdefg", 3, 1)
    assert got == ["a", "b", "c"], f"page('abcdefg', 3, 1) lieferte {got!r}"


def test_second_page():
    """Seite 2 enthält die nächsten drei"""
    got = page("abcdefg", 3, 2)
    assert got == ["d", "e", "f"], f"page('abcdefg', 3, 2) lieferte {got!r}, erwartet: ['d', 'e', 'f']"


def test_short_last_page():
    """Die letzte Seite darf kürzer sein"""
    got = page("abcdefg", 3, 3)
    assert got == ["g"], f"page('abcdefg', 3, 3) lieferte {got!r}, erwartet: ['g']"


def test_no_such_page():
    """Eine Seite hinter dem Ende ist leer"""
    got = page("abcdefg", 3, 4)
    assert got == [], f"page('abcdefg', 3, 4) lieferte {got!r}, erwartet: []"


def test_generator():
    """Auch ein Generator funktioniert"""
    got = page((str(n) for n in range(100)), 10, 2)
    expected = [str(n) for n in range(10, 20)]
    assert got == expected, f"Seite 2 eines Generators mit 100 Zahlen war {got!r}, erwartet: {expected!r}"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Häufige Fehler

Ein negativer Index in islice

from itertools import islice

scores = [70, 85, 90, 65, 88]
print(list(islice(scores, -2, None)))

Was Python ausgibt

ValueError: Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize.

Warum, und die Lösung

islice arbeitet auf Iteratoren, die ihre Länge nicht kennen, vom Ende zu zählen ist also unmöglich. Bei einer Liste schneiden Sie normal: scores[-2:]. Bei einem Datenstrom behält deque(it, maxlen=2) die letzten Elemente; Sie lernen es später in diesem Modul in der Lektion zu collections kennen.

Das Ergebnis von chain indizieren

from itertools import chain

names = chain(["Ada", "Ben"], ["Cy"])
print(names[0])

Was Python ausgibt

TypeError: 'itertools.chain' object is not subscriptable

Warum, und die Lösung

chain liefert einen Iterator, keine Liste, und hat darum keine Positionen. Nehmen Sie Elemente mit next(names), oder bauen Sie zuerst eine Liste, wenn Sie Indizes brauchen: names = list(chain(...)). Das gilt für jede itertools-Funktion dieser Lektion.

Ein unvollständiges Stück mit strict=True

from itertools import batched

for pair in batched("abcde", 2, strict=True):
    print(pair)

Was Python ausgibt

ValueError: batched(): incomplete batch

Warum, und die Lösung

strict=True verlangt, dass batched ein kurzes letztes Stück ablehnt, und fünf Elemente lassen sich nicht in Paare teilen. Die ersten beiden Paare erscheinen noch vor dem Fehler. Lassen Sie strict weg, wenn ein kürzeres letztes Stück in Ordnung ist, oder prüfen Sie, ob die Länge aufgeht.

Python im Browser: Pyodide 314.0.7, MPL-2.0. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Verbinden, schneiden und stückeln

chain(a, b) durchläuft erst a, dann b, als ein Iterator, ohne eine neue Liste zu bauen. islice(it, stop) oder islice(it, start, stop, step) schneidet jedes Iterable, auch einen Generator oder einen endlosen, was [ : ] nicht kann; negative Werte sind nicht erlaubt. batched(it, n) fasst die Elemente zu Tupeln mit je n zusammen, das letzte Tupel darf kürzer sein. Jede itertools-Funktion dieser Lektion liefert einen Iterator: verzögert, gut für einen Durchlauf, und erst eine Liste, wenn Sie list() darauf aufrufen.

groupby braucht sortierte Eingaben

groupby(iterable, key) liefert Paare (key, group) für Folgen benachbarter Elemente mit demselben Schlüssel. Gleiche Schlüssel aus der ganzen Eingabe sammelt es nicht ein: In "AABAA" bilden die A zwei Gruppen. Sortieren Sie also zuerst nach demselben Schlüssel: groupby(sorted(data, key=f), key=f). Jede Gruppe ist ein Iterator, der sich die Eingabe mit groupby teilt. Sobald groupby zur nächsten Gruppe geht, ist die vorige weg; machen Sie eine Gruppe also sofort zur Liste oder summieren Sie sie.

Laufende Ergebnisse und Kombinationen

accumulate(it) liefert laufende Summen: 1, 3, 6 für [1, 2, 3]. Mit einer Funktion zweier Argumente als zweitem Argument führt es ein anderes laufendes Ergebnis: accumulate(prices, max) liefert den bisher höchsten Preis. initial=100 stellt einen Startwert voran. product(a, b) liefert jedes Paar (x, y) in der Reihenfolge zweier verschachtelter for-Schleifen, wobei sich das rechte Element am schnellsten ändert. product(a, repeat=2) paart a mit sich selbst.

Quellen

Zuletzt geprüft am 29. September 2026