itertools — Funktionen zur Erstellung von Iteratoren für effiziente Schleifen


Dieses Modul implementiert eine Reihe von Iterator-Bausteinen, inspiriert von Konstrukten aus APL, Haskell und SML. Jede wurde in einer für Python geeigneten Form umgestaltet.

Das Modul standardisiert einen Kernsatz schneller, speichereffizienter Werkzeuge, die für sich allein oder in Kombination nützlich sind. Zusammen bilden sie eine „Iterator-Algebra“, die es ermöglicht, spezialisierte Werkzeuge prägnant und effizient in reinem Python zu konstruieren.

Zum Beispiel bietet SML ein Tabellierungswerkzeug: tabulate(f), das eine Sequenz f(0), f(1), ... erzeugt. Derselbe Effekt kann in Python erzielt werden, indem map() und count() kombiniert werden, um map(f, count()) zu bilden.

Endlose Iteratoren

Iterator

Argumente

Ergebnisse

Beispiel

count()

[start[, step]]

start, start+step, start+2*step, …

count(10) 10 11 12 13 14 ...

cycle()

p

p0, p1, … plast, p0, p1, …

cycle('ABCD') A B C D A B C D ...

repeat()

elem [,n]

elem, elem, elem, … endlos oder bis zu n Mal

repeat(10, 3) 10 10 10

Iteratoren, die bei der kürzesten Eingabesequenz enden

Iterator

Argumente

Ergebnisse

Beispiel

accumulate()

p [,func]

p0, p0+p1, p0+p1+p2, …

accumulate([1,2,3,4,5]) 1 3 6 10 15

batched()

p, n

(p0, p1, …, p_n-1), …

batched('ABCDEFG', n=2) AB CD EF G

chain()

p, q, …

p0, p1, … plast, q0, q1, …

chain('ABC', 'DEF') A B C D E F

chain.from_iterable()

iterable

p0, p1, … plast, q0, q1, …

chain.from_iterable(['ABC', 'DEF']) A B C D E F

compress()

data, selectors

(d[0] if s[0]), (d[1] if s[1]), …

compress('ABCDEF', [1,0,1,0,1,1]) A C E F

dropwhile()

predicate, seq

seq[n], seq[n+1], beginnend, wenn das Prädikat fehlschlägt

dropwhile(lambda x: x<5, [1,4,6,3,8]) 6 3 8

filterfalse()

predicate, seq

Elemente von seq, bei denen predicate(elem) fehlschlägt

filterfalse(lambda x: x<5, [1,4,6,3,8]) 6 8

groupby()

iterable[, key]

Sub-Iteratoren, gruppiert nach dem Wert von key(v)

groupby(['A','B','DEF'], len) (1, A B) (3, DEF)

islice()

seq, [start,] stop [, step]

Elemente aus seq[start:stop:step]

islice('ABCDEFG', 2, None) C D E F G

pairwise()

iterable

(p[0], p[1]), (p[1], p[2])

pairwise('ABCDEFG') AB BC CD DE EF FG

starmap()

func, seq

func(*seq[0]), func(*seq[1]), …

starmap(pow, [(2,5), (3,2), (10,3)]) 32 9 1000

takewhile()

predicate, seq

seq[0], seq[1], bis das Prädikat fehlschlägt

takewhile(lambda x: x<5, [1,4,6,3,8]) 1 4

tee()

it, n

it1, it2, … itn teilt einen Iterator in n auf

tee('ABC', 2) A B C, A B C

zip_longest()

p, q, …

(p[0], q[0]), (p[1], q[1]), …

zip_longest('ABCD', 'xy', fillvalue='-') Ax By C- D-

Kombinatorische Iteratoren

Iterator

Argumente

Ergebnisse

product()

p, q, … [repeat=1]

kartesisches Produkt, äquivalent zu einer verschachtelten for-Schleife

permutations()

p[, r]

r-längige Tupel, alle möglichen Ordnungen, keine wiederholten Elemente

combinations()

p, r

r-längige Tupel, in sortierter Reihenfolge, keine wiederholten Elemente

combinations_with_replacement()

p, r

r-längige Tupel, in sortierter Reihenfolge, mit wiederholten Elementen

Beispiele

Ergebnisse

product('ABCD', repeat=2)

AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD

permutations('ABCD', 2)

AB AC AD BA BC BD CA CB CD DA DB DC

combinations('ABCD', 2)

AB AC AD BC BD CD

combinations_with_replacement('ABCD', 2)

AA AB AC AD BB BC BD CC CD DD

Itertool-Funktionen

Die folgenden Funktionen erstellen und geben Iteratoren zurück. Einige liefern Ströme unendlicher Länge, daher sollten sie nur von Funktionen oder Schleifen aufgerufen werden, die den Strom abschneiden.

itertools.accumulate(iterable[, function, *, initial=None])

Erstellt einen Iterator, der akkumulierte Summen oder akkumulierte Ergebnisse von anderen binären Funktionen zurückgibt.

Die function ist standardmäßig die Addition. Die function sollte zwei Argumente akzeptieren: eine akkumulierte Summe und einen Wert aus dem iterable.

Wenn ein initial-Wert angegeben wird, beginnt die Akkumulation mit diesem Wert, und die Ausgabe hat ein Element mehr als das Eingabe-Iterable.

Etwa äquivalent zu

def accumulate(iterable, function=operator.add, *, initial=None):
    'Return running totals'
    # accumulate([1,2,3,4,5]) → 1 3 6 10 15
    # accumulate([1,2,3,4,5], initial=100) → 100 101 103 106 110 115
    # accumulate([1,2,3,4,5], operator.mul) → 1 2 6 24 120

    iterator = iter(iterable)
    total = initial
    if initial is None:
        try:
            total = next(iterator)
        except StopIteration:
            return

    yield total
    for element in iterator:
        total = function(total, element)
        yield total

Um ein laufendes Minimum zu berechnen, setzen Sie function auf min(). Für ein laufendes Maximum setzen Sie function auf max(). Oder um ein laufendes Produkt zu bilden, setzen Sie function auf operator.mul(). Um eine Amortisationstabelle zu erstellen, akkumulieren Sie die Zinsen und wenden Sie Zahlungen an.

>>> data = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8]
>>> list(accumulate(data, max))              # running maximum
[3, 4, 6, 6, 6, 9, 9, 9, 9, 9]
>>> list(accumulate(data, operator.mul))     # running product
[3, 12, 72, 144, 144, 1296, 0, 0, 0, 0]

# Amortize a 5% loan of 1000 with 10 annual payments of 90
>>> update = lambda balance, payment: round(balance * 1.05) - payment
>>> list(accumulate(repeat(90, 10), update, initial=1_000))
[1000, 960, 918, 874, 828, 779, 728, 674, 618, 559, 497]

Siehe functools.reduce() für eine ähnliche Funktion, die nur den endgültigen akkumulierten Wert zurückgibt.

Hinzugefügt in Version 3.2.

Geändert in Version 3.3: Optionaler Parameter function hinzugefügt.

Geändert in Version 3.8: Optionaler Parameter initial hinzugefügt.

itertools.batched(iterable, n, *, strict=False)

Batcht Daten aus dem iterable in Tupel der Länge n. Der letzte Batch kann kürzer als n sein.

Wenn strict auf True gesetzt ist, wird ein ValueError ausgelöst, wenn der letzte Batch kürzer als n ist.

Schleift über das Eingabe-Iterable und sammelt Daten in Tupel bis zur Größe n. Die Eingabe wird träge verbraucht, gerade genug, um einen Batch zu füllen. Das Ergebnis wird ausgegeben, sobald der Batch voll ist oder wenn das Eingabe-Iterable erschöpft ist.

>>> flattened_data = ['roses', 'red', 'violets', 'blue', 'sugar', 'sweet']
>>> unflattened = list(batched(flattened_data, 2))
>>> unflattened
[('roses', 'red'), ('violets', 'blue'), ('sugar', 'sweet')]

Etwa äquivalent zu

def batched(iterable, n, *, strict=False):
    # batched('ABCDEFG', 2) → AB CD EF G
    if n < 1:
        raise ValueError('n must be at least one')
    iterator = iter(iterable)
    while batch := tuple(islice(iterator, n)):
        if strict and len(batch) != n:
            raise ValueError('batched(): incomplete batch')
        yield batch

Hinzugefügt in Version 3.12.

Geändert in Version 3.13: Die Option strict wurde hinzugefügt.

itertools.chain(*iterables)

Erstellt einen Iterator, der Elemente aus dem ersten Iterable zurückgibt, bis es erschöpft ist, und dann zum nächsten Iterable übergeht, bis alle Iterables erschöpft sind. Dies kombiniert mehrere Datenquellen zu einem einzigen Iterator. Ungefähr äquivalent zu

def chain(*iterables):
    # chain('ABC', 'DEF') → A B C D E F
    for iterable in iterables:
        yield from iterable
classmethod chain.from_iterable(iterable)

Alternativer Konstruktor für chain(). Erhält verkettete Eingaben aus einem einzigen Iterable-Argument, das träge ausgewertet wird. Ungefähr äquivalent zu

def from_iterable(iterables):
    # chain.from_iterable(['ABC', 'DEF']) → A B C D E F
    for iterable in iterables:
        yield from iterable
itertools.combinations(iterable, r)

Gibt Untersequenzen der Elemente aus dem Eingabe-iterable der Länge r zurück.

Die Ausgabe ist eine Untermenge von product(), die nur Einträge enthält, die Untersequenzen des iterable sind. Die Länge der Ausgabe wird durch math.comb() bestimmt, die n! / r! / (n - r)! berechnet, wenn 0 r n, oder Null, wenn r > n.

Die Kombinations-Tupel werden in lexikografischer Reihenfolge entsprechend der Reihenfolge des Eingabe-iterable ausgegeben. Wenn das Eingabe-iterable sortiert ist, werden die Ausgabe-Tupel in sortierter Reihenfolge erzeugt.

Elemente werden nach ihrer Position und nicht nach ihrem Wert als einzigartig behandelt. Wenn die Eingabeelemente eindeutig sind, gibt es keine wiederholten Werte innerhalb jeder Kombination.

Etwa äquivalent zu

def combinations(iterable, r):
    # combinations('ABCD', 2) → AB AC AD BC BD CD
    # combinations(range(4), 3) → 012 013 023 123

    pool = tuple(iterable)
    n = len(pool)
    if r > n:
        return
    indices = list(range(r))

    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield tuple(pool[i] for i in indices)
itertools.combinations_with_replacement(iterable, r)

Gibt Untersequenzen der Elemente aus dem Eingabe-iterable der Länge r zurück, wobei einzelne Elemente mehrfach wiederholt werden können.

Die Ausgabe ist eine Untermenge von product(), die nur Einträge enthält, die Untersequenzen (mit möglichen wiederholten Elementen) des iterable sind. Die Anzahl der zurückgegebenen Untersequenzen ist (n + r - 1)! / r! / (n - 1)!, wenn n > 0.

Die Kombinations-Tupel werden in lexikografischer Reihenfolge entsprechend der Reihenfolge des Eingabe-iterable ausgegeben. Wenn das Eingabe-iterable sortiert ist, werden die Ausgabe-Tupel in sortierter Reihenfolge erzeugt.

Elemente werden nach ihrer Position und nicht nach ihrem Wert als einzigartig behandelt. Wenn die Eingabeelemente eindeutig sind, sind die erzeugten Kombinationen ebenfalls eindeutig.

Etwa äquivalent zu

def combinations_with_replacement(iterable, r):
    # combinations_with_replacement('ABC', 2) → AA AB AC BB BC CC

    pool = tuple(iterable)
    n = len(pool)
    if not n and r:
        return
    indices = [0] * r

    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != n - 1:
                break
        else:
            return
        indices[i:] = [indices[i] + 1] * (r - i)
        yield tuple(pool[i] for i in indices)

Hinzugefügt in Version 3.1.

itertools.compress(data, selectors)

Erstellt einen Iterator, der Elemente aus data zurückgibt, bei denen das entsprechende Element in selectors wahr ist. Stoppt, wenn entweder das data- oder das selectors-Iterable erschöpft ist. Ungefähr äquivalent zu

def compress(data, selectors):
    # compress('ABCDEF', [1,0,1,0,1,1]) → A C E F
    return (datum for datum, selector in zip(data, selectors) if selector)

Hinzugefügt in Version 3.1.

itertools.count(start=0, step=1)

Erstellt einen Iterator, der gleichmäßig beabstandete Werte ab start zurückgibt. Kann mit map() zum Generieren aufeinanderfolgender Datenpunkte oder mit zip() zum Hinzufügen von Sequenznummern verwendet werden. Ungefähr äquivalent zu

def count(start=0, step=1):
    # count(10) → 10 11 12 13 14 ...
    # count(2.5, 0.5) → 2.5 3.0 3.5 ...
    n = start
    while True:
        yield n
        n += step

Beim Zählen mit Gleitkommazahlen kann manchmal eine bessere Genauigkeit durch Ersetzen des Codes durch Multiplikation erreicht werden, wie z. B.: (start + step * i for i in count()).

Geändert in Version 3.1: Argument step hinzugefügt und nicht-ganzzahlige Argumente erlaubt.

itertools.cycle(iterable)

Erstellt einen Iterator, der Elemente aus dem iterable zurückgibt und eine Kopie von jedem speichert. Wenn das Iterable erschöpft ist, werden Elemente aus der gespeicherten Kopie zurückgegeben. Wiederholt sich endlos. Ungefähr äquivalent zu

def cycle(iterable):
    # cycle('ABCD') → A B C D A B C D A B C D ...

    saved = []
    for element in iterable:
        yield element
        saved.append(element)

    while saved:
        for element in saved:
            yield element

Dieses Itertool kann erhebliche Hilfsspeicher benötigen (abhängig von der Länge des Iterables).

itertools.dropwhile(predicate, iterable)

Erstellt einen Iterator, der Elemente aus dem iterable verwirft, solange das predicate wahr ist, und danach jedes Element zurückgibt. Ungefähr äquivalent zu

def dropwhile(predicate, iterable):
    # dropwhile(lambda x: x<5, [1,4,6,3,8]) → 6 3 8

    iterator = iter(iterable)
    for x in iterator:
        if not predicate(x):
            yield x
            break

    for x in iterator:
        yield x

Beachten Sie, dass dies erst dann *irgendwelche* Ausgaben erzeugt, wenn das Prädikat zum ersten Mal falsch wird, sodass dieses Itertool eine langwierige Startzeit haben kann.

itertools.filterfalse(predicate, iterable)

Erstellt einen Iterator, der Elemente aus dem iterable filtert und nur diejenigen zurückgibt, für die das predicate einen falschen Wert zurückgibt. Wenn predicate None ist, werden die Elemente zurückgegeben, die falsch sind. Ungefähr äquivalent zu

def filterfalse(predicate, iterable):
    # filterfalse(lambda x: x<5, [1,4,6,3,8]) → 6 8

    if predicate is None:
        predicate = bool

    for x in iterable:
        if not predicate(x):
            yield x
itertools.groupby(iterable, key=None)

Erstellt einen Iterator, der aufeinanderfolgende Schlüssel und Gruppen aus dem iterable zurückgibt. Der key ist eine Funktion, die einen Schlüsselwert für jedes Element berechnet. Wenn nicht angegeben oder None ist, ist key eine Identitätsfunktion und gibt das Element unverändert zurück. Im Allgemeinen muss das Iterable bereits auf denselben Schlüssel angewendet worden sein.

Die Funktionsweise von groupby() ähnelt dem `uniq`-Filter unter Unix. Er erzeugt einen Bruch oder eine neue Gruppe, jedes Mal, wenn sich der Wert der Schlüsselfunktion ändert (weshalb es normalerweise notwendig ist, die Daten mit derselben Schlüsselfunktion sortiert zu haben). Dieses Verhalten unterscheidet sich von SQLs GROUP BY, das gemeinsame Elemente aggregiert, unabhängig von ihrer Eingabereihenfolge.

Die zurückgegebene Gruppe ist selbst ein Iterator, der das zugrunde liegende Iterable mit groupby() teilt. Da die Quelle geteilt wird, wenn das groupby()-Objekt fortgeschaltet wird, ist die vorherige Gruppe nicht mehr sichtbar. Wenn diese Daten später benötigt werden, sollten sie als Liste gespeichert werden.

groups = []
uniquekeys = []
data = sorted(data, key=keyfunc)
for k, g in groupby(data, keyfunc):
    groups.append(list(g))      # Store group iterator as a list
    uniquekeys.append(k)

groupby() ist ungefähr äquivalent zu

def groupby(iterable, key=None):
    # [k for k, g in groupby('AAAABBBCCDAABBB')] → A B C D A B
    # [list(g) for k, g in groupby('AAAABBBCCD')] → AAAA BBB CC D

    keyfunc = (lambda x: x) if key is None else key
    iterator = iter(iterable)
    exhausted = False

    def _grouper(target_key):
        nonlocal curr_value, curr_key, exhausted
        yield curr_value
        for curr_value in iterator:
            curr_key = keyfunc(curr_value)
            if curr_key != target_key:
                return
            yield curr_value
        exhausted = True

    try:
        curr_value = next(iterator)
    except StopIteration:
        return
    curr_key = keyfunc(curr_value)

    while not exhausted:
        target_key = curr_key
        curr_group = _grouper(target_key)
        yield curr_key, curr_group
        if curr_key == target_key:
            for _ in curr_group:
                pass
itertools.islice(iterable, stop)
itertools.islice(iterable, start, stop[, step])

Erstellt einen Iterator, der ausgewählte Elemente aus dem Iterable zurückgibt. Funktioniert wie Sequenz-Slicing, unterstützt aber keine negativen Werte für start, stop oder step.

Wenn start Null oder None ist, beginnt die Iteration bei Null. Andernfalls werden Elemente aus dem Iterable übersprungen, bis start erreicht ist.

Wenn stop None ist, wird die Iteration fortgesetzt, bis das Eingabe-Iterable erschöpft ist (falls überhaupt). Andernfalls stoppt sie an der angegebenen Position.

Wenn step None ist, ist die Schrittweite standardmäßig eins. Elemente werden nacheinander zurückgegeben, es sei denn, step wird auf einen höheren Wert als eins gesetzt, was dazu führt, dass Elemente übersprungen werden.

Etwa äquivalent zu

def islice(iterable, *args):
    # islice('ABCDEFG', 2) → A B
    # islice('ABCDEFG', 2, 4) → C D
    # islice('ABCDEFG', 2, None) → C D E F G
    # islice('ABCDEFG', 0, None, 2) → A C E G

    s = slice(*args)
    start = 0 if s.start is None else s.start
    stop = s.stop
    step = 1 if s.step is None else s.step
    if start < 0 or (stop is not None and stop < 0) or step <= 0:
        raise ValueError

    indices = count() if stop is None else range(max(start, stop))
    next_i = start
    for i, element in zip(indices, iterable):
        if i == next_i:
            yield element
            next_i += step

Wenn die Eingabe ein Iterator ist, dann schreitet das vollständige Verbrauch des islice den Eingabe-Iterator um `max(start, stop)` Schritte voran, unabhängig vom Wert von step.

itertools.pairwise(iterable)

Gibt aufeinanderfolgende überlappende Paare aus dem Eingabe-iterable zurück.

Die Anzahl der 2-Tupel im Ausgabe-Iterator wird um eins geringer sein als die Anzahl der Eingaben. Er ist leer, wenn das Eingabe-Iterable weniger als zwei Werte hat.

Etwa äquivalent zu

def pairwise(iterable):
    # pairwise('ABCDEFG') → AB BC CD DE EF FG

    iterator = iter(iterable)
    a = next(iterator, None)

    for b in iterator:
        yield a, b
        a = b

Hinzugefügt in Version 3.10.

itertools.permutations(iterable, r=None)

Gibt aufeinanderfolgende Permutationen der Elemente aus dem iterable der Länge r zurück.

Wenn r nicht angegeben oder None ist, dann ist r standardmäßig die Länge des iterable und alle möglichen Permutationen in voller Länge werden erzeugt.

Die Ausgabe ist eine Untermenge von product(), bei der Einträge mit wiederholten Elementen herausgefiltert wurden. Die Länge der Ausgabe wird durch math.perm() bestimmt, die n! / (n - r)! berechnet, wenn 0 r n, oder Null, wenn r > n.

Die Permutations-Tupel werden in lexikografischer Reihenfolge entsprechend der Reihenfolge des Eingabe-iterable ausgegeben. Wenn das Eingabe-iterable sortiert ist, werden die Ausgabe-Tupel in sortierter Reihenfolge erzeugt.

Elemente werden nach ihrer Position und nicht nach ihrem Wert als einzigartig behandelt. Wenn die Eingabeelemente eindeutig sind, gibt es keine wiederholten Werte innerhalb einer Permutation.

Etwa äquivalent zu

def permutations(iterable, r=None):
    # permutations('ABCD', 2) → AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) → 012 021 102 120 201 210

    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r > n:
        return

    indices = list(range(n))
    cycles = list(range(n, n-r, -1))
    yield tuple(pool[i] for i in indices[:r])

    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return
itertools.product(*iterables, repeat=1)

Kartesisches Produkt der Eingabe-Iterables.

Ungefähr äquivalent zu verschachtelten for-Schleifen in einem Generatorausdruck. Zum Beispiel gibt product(A, B) dasselbe zurück wie ((x,y) for x in A for y in B).

Die verschachtelten Schleifen zählen wie ein Tachometer, wobei das rechteste Element bei jeder Iteration vorrückt. Dieses Muster erzeugt eine lexikografische Reihenfolge, so dass, wenn die Iterables der Eingabe sortiert sind, die Produkt-Tupel in sortierter Reihenfolge ausgegeben werden.

Um das Produkt eines Iterables mit sich selbst zu berechnen, geben Sie die Anzahl der Wiederholungen mit dem optionalen Schlüsselwortargument repeat an. Zum Beispiel bedeutet product(A, repeat=4) dasselbe wie product(A, A, A, A).

Diese Funktion ist ungefähr äquivalent zum folgenden Code, außer dass die tatsächliche Implementierung keine Zwischenergebnisse im Speicher aufbaut.

def product(*iterables, repeat=1):
    # product('ABCD', 'xy') → Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) → 000 001 010 011 100 101 110 111

    if repeat < 0:
        raise ValueError('repeat argument cannot be negative')
    pools = [tuple(pool) for pool in iterables] * repeat

    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]

    for prod in result:
        yield tuple(prod)

Bevor product() ausgeführt wird, verbraucht es die Eingabe-Iterables vollständig und hält Wertesparkassen im Speicher, um die Produkte zu generieren. Daher ist es nur mit endlichen Eingaben nützlich.

itertools.repeat(object[, times])

Erstellt einen Iterator, der object immer wieder zurückgibt. Läuft endlos, es sei denn, das Argument times wird angegeben.

Etwa äquivalent zu

def repeat(object, times=None):
    # repeat(10, 3) → 10 10 10
    if times is None:
        while True:
            yield object
    else:
        for i in range(times):
            yield object

Eine häufige Verwendung für repeat ist die Bereitstellung eines Stroms konstanter Werte für map oder zip.

>>> list(map(pow, range(10), repeat(2)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
itertools.starmap(function, iterable)

Erstellt einen Iterator, der die function berechnet, indem Argumente aus dem iterable verwendet werden. Wird anstelle von map() verwendet, wenn Argumentparameter bereits in Tupel "vorverpackt" wurden.

Der Unterschied zwischen map() und starmap() entspricht dem Unterschied zwischen function(a,b) und function(*c). Ungefähr äquivalent zu

def starmap(function, iterable):
    # starmap(pow, [(2,5), (3,2), (10,3)]) → 32 9 1000
    for args in iterable:
        yield function(*args)
itertools.takewhile(predicate, iterable)

Erstellt einen Iterator, der Elemente aus dem iterable zurückgibt, solange das predicate wahr ist. Ungefähr äquivalent zu

def takewhile(predicate, iterable):
    # takewhile(lambda x: x<5, [1,4,6,3,8]) → 1 4
    for x in iterable:
        if not predicate(x):
            break
        yield x

Beachten Sie, dass das Element, das zuerst die Prädikatbedingung nicht erfüllt, aus dem Eingabe-Iterator verbraucht wird und kein Zugriff darauf möglich ist. Dies kann ein Problem darstellen, wenn eine Anwendung den Eingabe-Iterator nach der vollständigen Ausführung von takewhile weiter verbrauchen möchte. Um dieses Problem zu lösen, sollten Sie more-itertools before_and_after() in Betracht ziehen.

itertools.tee(iterable, n=2)

Gibt n unabhängige Iteratoren aus einem einzigen Iterable zurück.

Etwa äquivalent zu

def tee(iterable, n=2):
    if n < 0:
        raise ValueError
    if n == 0:
        return ()
    iterator = _tee(iterable)
    result = [iterator]
    for _ in range(n - 1):
        result.append(_tee(iterator))
    return tuple(result)

class _tee:

    def __init__(self, iterable):
        it = iter(iterable)
        if isinstance(it, _tee):
            self.iterator = it.iterator
            self.link = it.link
        else:
            self.iterator = it
            self.link = [None, None]

    def __iter__(self):
        return self

    def __next__(self):
        link = self.link
        if link[1] is None:
            link[0] = next(self.iterator)
            link[1] = [None, None]
        value, self.link = link
        return value

Wenn das Eingabe-iterable bereits ein Tee-Iterator-Objekt ist, werden alle Mitglieder des Rückgabe-Tupels so erstellt, als wären sie vom vorgelagerten tee()-Aufruf erzeugt worden. Dieser "Flattening-Schritt" ermöglicht es verschachtelten tee()-Aufrufen, dieselbe zugrunde liegende Datenkette zu teilen und einen einzigen Aktualisierungsschritt anstelle einer Kette von Aufrufen zu haben.

Die Flattening-Eigenschaft macht Tee-Iteratoren zu effizient inspizierbaren Iteratoren.

def lookahead(tee_iterator):
     "Return the next value without moving the input forward"
     [forked_iterator] = tee(tee_iterator, 1)
     return next(forked_iterator)
>>> iterator = iter('abcdef')
>>> [iterator] = tee(iterator, 1)   # Make the input peekable
>>> next(iterator)                  # Move the iterator forward
'a'
>>> lookahead(iterator)             # Check next value
'b'
>>> next(iterator)                  # Continue moving forward
'b'

tee-Iteratoren sind nicht threadsicher. Ein RuntimeError kann ausgelöst werden, wenn gleichzeitig Iteratoren verwendet werden, die von demselben tee()-Aufruf zurückgegeben wurden, selbst wenn das ursprüngliche Iterable threadsicher ist.

Dieses itertools-Modul benötigt möglicherweise erheblichen Hilfsspeicher (abhängig davon, wie viele temporäre Daten gespeichert werden müssen). Im Allgemeinen ist es schneller, list() anstelle von tee() zu verwenden, wenn ein Iterator die Daten weitgehend oder vollständig verbraucht, bevor ein anderer Iterator beginnt.

itertools.zip_longest(*iterables, fillvalue=None)

Erstellt einen Iterator, der Elemente aus jedem der Iterables aggregiert.

Wenn die Iterables ungleiche Längen haben, werden fehlende Werte mit fillvalue aufgefüllt. Wenn nicht angegeben, ist fillvalue standardmäßig None.

Die Iteration wird fortgesetzt, bis das längste Iterable erschöpft ist.

Etwa äquivalent zu

def zip_longest(*iterables, fillvalue=None):
    # zip_longest('ABCD', 'xy', fillvalue='-') → Ax By C- D-

    iterators = list(map(iter, iterables))
    num_active = len(iterators)
    if not num_active:
        return

    while True:
        values = []
        for i, iterator in enumerate(iterators):
            try:
                value = next(iterator)
            except StopIteration:
                num_active -= 1
                if not num_active:
                    return
                iterators[i] = repeat(fillvalue)
                value = fillvalue
            values.append(value)
        yield tuple(values)

Wenn eines der Iterables potenziell unendlich ist, sollte die Funktion zip_longest() mit etwas umhüllt werden, das die Anzahl der Aufrufe begrenzt (z. B. islice() oder takewhile()).

Itertools-Rezepte

Dieser Abschnitt zeigt Rezepte zur Erstellung eines erweiterten Werkzeugkastens unter Verwendung der bestehenden itertools-Module als Bausteine.

Der Hauptzweck der itertools-Rezepte ist die Bildung. Die Rezepte zeigen verschiedene Denkweisen über einzelne Werkzeuge an – zum Beispiel, dass chain.from_iterable mit dem Konzept des Abflachens zusammenhängt. Die Rezepte geben auch Ideen, wie die Werkzeuge kombiniert werden können – zum Beispiel, wie starmap() und repeat() zusammenarbeiten können. Die Rezepte zeigen auch Muster für die Verwendung von itertools mit den Modulen operator und collections sowie mit den integrierten itertools wie map(), filter(), reversed() und enumerate().

Ein sekundärer Zweck der Rezepte ist die Funktion als Brutstätte. Die itertools-Module accumulate(), compress() und pairwise() haben als Rezepte begonnen. Derzeit werden die Rezepte sliding_window(), iter_index() und sieve() getestet, um zu sehen, ob sie ihren Wert beweisen.

Nahezu alle diese Rezepte und viele, viele weitere können vom more-itertools-Projekt im Python Package Index installiert werden.

python -m pip install more-itertools

Viele der Rezepte bieten die gleiche hohe Leistung wie die zugrunde liegende Werkzeugsammlung. Überlegene Speicherleistung wird durch die Verarbeitung von Elementen einzeln anstatt durch das Laden des gesamten Iterables in den Speicher auf einmal beibehalten. Der Codeumfang wird klein gehalten, indem die Werkzeuge in einem funktionalen Stil verkettet werden. Hohe Geschwindigkeit wird beibehalten, indem "vektorisierte" Bausteine gegenüber der Verwendung von for-Schleifen und Generatoren, die Interpreter-Overhead verursachen, bevorzugt werden.

from collections import Counter, deque
from contextlib import suppress
from functools import reduce
from math import comb, prod, sumprod, isqrt
from operator import itemgetter, getitem, mul, neg

def take(n, iterable):
    "Return first n items of the iterable as a list."
    return list(islice(iterable, n))

def prepend(value, iterable):
    "Prepend a single value in front of an iterable."
    # prepend(1, [2, 3, 4]) → 1 2 3 4
    return chain([value], iterable)

def tabulate(function, start=0):
    "Return function(0), function(1), ..."
    return map(function, count(start))

def repeatfunc(function, times=None, *args):
    "Repeat calls to a function with specified arguments."
    if times is None:
        return starmap(function, repeat(args))
    return starmap(function, repeat(args, times))

def flatten(list_of_lists):
    "Flatten one level of nesting."
    return chain.from_iterable(list_of_lists)

def ncycles(iterable, n):
    "Returns the sequence elements n times."
    return chain.from_iterable(repeat(tuple(iterable), n))

def loops(n):
    "Loop n times. Like range(n) but without creating integers."
    # for _ in loops(100): ...
    return repeat(None, n)

def tail(n, iterable):
    "Return an iterator over the last n items."
    # tail(3, 'ABCDEFG') → E F G
    return iter(deque(iterable, maxlen=n))

def consume(iterator, n=None):
    "Advance the iterator n-steps ahead. If n is None, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        deque(iterator, maxlen=0)
    else:
        next(islice(iterator, n, n), None)

def nth(iterable, n, default=None):
    "Returns the nth item or a default value."
    return next(islice(iterable, n, None), default)

def quantify(iterable, predicate=bool):
    "Given a predicate that returns True or False, count the True results."
    return sum(map(predicate, iterable))

def first_true(iterable, default=False, predicate=None):
    "Returns the first true value or the *default* if there is no true value."
    # first_true([a,b,c], x) → a or b or c or x
    # first_true([a,b], x, f) → a if f(a) else b if f(b) else x
    return next(filter(predicate, iterable), default)

def all_equal(iterable, key=None):
    "Returns True if all the elements are equal to each other."
    # all_equal('4٤௪౪໔', key=int) → True
    return len(take(2, groupby(iterable, key))) <= 1

def unique_justseen(iterable, key=None):
    "Yield unique elements, preserving order. Remember only the element just seen."
    # unique_justseen('AAAABBBCCDAABBB') → A B C D A B
    # unique_justseen('ABBcCAD', str.casefold) → A B c A D
    if key is None:
        return map(itemgetter(0), groupby(iterable))
    return map(next, map(itemgetter(1), groupby(iterable, key)))

def unique_everseen(iterable, key=None):
    "Yield unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') → A B C D
    # unique_everseen('ABBcCAD', str.casefold) → A B c D
    seen = set()
    if key is None:
        for element in filterfalse(seen.__contains__, iterable):
            seen.add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen.add(k)
                yield element

def unique(iterable, key=None, reverse=False):
   "Yield unique elements in sorted order. Supports unhashable inputs."
   # unique([[1, 2], [3, 4], [1, 2]]) → [1, 2] [3, 4]
   sequenced = sorted(iterable, key=key, reverse=reverse)
   return unique_justseen(sequenced, key=key)

def sliding_window(iterable, n):
    "Collect data into overlapping fixed-length chunks or blocks."
    # sliding_window('ABCDEFG', 4) → ABCD BCDE CDEF DEFG
    iterator = iter(iterable)
    window = deque(islice(iterator, n - 1), maxlen=n)
    for x in iterator:
        window.append(x)
        yield tuple(window)

def grouper(iterable, n, *, incomplete='fill', fillvalue=None):
    "Collect data into non-overlapping fixed-length chunks or blocks."
    # grouper('ABCDEFG', 3, fillvalue='x') → ABC DEF Gxx
    # grouper('ABCDEFG', 3, incomplete='strict') → ABC DEF ValueError
    # grouper('ABCDEFG', 3, incomplete='ignore') → ABC DEF
    iterators = [iter(iterable)] * n
    match incomplete:
        case 'fill':
            return zip_longest(*iterators, fillvalue=fillvalue)
        case 'strict':
            return zip(*iterators, strict=True)
        case 'ignore':
            return zip(*iterators)
        case _:
            raise ValueError('Expected fill, strict, or ignore')

def roundrobin(*iterables):
    "Visit input iterables in a cycle until each is exhausted."
    # roundrobin('ABC', 'D', 'EF') → A D E B F C
    # Algorithm credited to George Sakkis
    iterators = map(iter, iterables)
    for num_active in range(len(iterables), 0, -1):
        iterators = cycle(islice(iterators, num_active))
        yield from map(next, iterators)

def subslices(seq):
    "Return all contiguous non-empty subslices of a sequence."
    # subslices('ABCD') → A AB ABC ABCD B BC BCD C CD D
    slices = starmap(slice, combinations(range(len(seq) + 1), 2))
    return map(getitem, repeat(seq), slices)

def iter_index(iterable, value, start=0, stop=None):
    "Return indices where a value occurs in a sequence or iterable."
    # iter_index('AABCADEAF', 'A') → 0 1 4 7
    seq_index = getattr(iterable, 'index', None)
    if seq_index is None:
        iterator = islice(iterable, start, stop)
        for i, element in enumerate(iterator, start):
            if element is value or element == value:
                yield i
    else:
        stop = len(iterable) if stop is None else stop
        i = start
        with suppress(ValueError):
            while True:
                yield (i := seq_index(value, i, stop))
                i += 1

def iter_except(function, exception, first=None):
    "Convert a call-until-exception interface to an iterator interface."
    # iter_except(d.popitem, KeyError) → non-blocking dictionary iterator
    with suppress(exception):
        if first is not None:
            yield first()
        while True:
            yield function()

Die folgenden Rezepte haben einen eher mathematischen Charakter

def multinomial(*counts):
    "Number of distinct arrangements of a multiset."
    # Counter('abracadabra').values() → 5 2 2 1 1
    # multinomial(5, 2, 2, 1, 1) → 83160
    return prod(map(comb, accumulate(counts), counts))

def powerset(iterable):
    "Subsequences of the iterable from shortest to longest."
    # powerset([1,2,3]) → () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def sum_of_squares(iterable):
    "Add up the squares of the input values."
    # sum_of_squares([10, 20, 30]) → 1400
    return sumprod(*tee(iterable))

def reshape(matrix, columns):
    "Reshape a 2-D matrix to have a given number of columns."
    # reshape([(0, 1), (2, 3), (4, 5)], 3) →  (0, 1, 2), (3, 4, 5)
    return batched(chain.from_iterable(matrix), columns, strict=True)

def transpose(matrix):
    "Swap the rows and columns of a 2-D matrix."
    # transpose([(1, 2, 3), (11, 22, 33)]) → (1, 11) (2, 22) (3, 33)
    return zip(*matrix, strict=True)

def matmul(m1, m2):
    "Multiply two matrices."
    # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80), (41, 60)
    n = len(m2[0])
    return batched(starmap(sumprod, product(m1, transpose(m2))), n)

def convolve(signal, kernel):
    """Discrete linear convolution of two iterables.
    Equivalent to polynomial multiplication.

    Convolutions are mathematically commutative; however, the inputs are
    evaluated differently.  The signal is consumed lazily and can be
    infinite. The kernel is fully consumed before the calculations begin.

    Article:  https://betterexplained.com/articles/intuitive-convolution/
    Video:    https://www.youtube.com/watch?v=KuXjwB4LzSA
    """
    # convolve([1, -1, -20], [1, -3]) → 1 -4 -17 60
    # convolve(data, [0.25, 0.25, 0.25, 0.25]) → Moving average (blur)
    # convolve(data, [1/2, 0, -1/2]) → 1st derivative estimate
    # convolve(data, [1, -2, 1]) → 2nd derivative estimate
    kernel = tuple(kernel)[::-1]
    n = len(kernel)
    padded_signal = chain(repeat(0, n-1), signal, repeat(0, n-1))
    windowed_signal = sliding_window(padded_signal, n)
    return map(sumprod, repeat(kernel), windowed_signal)

def polynomial_from_roots(roots):
    """Compute a polynomial's coefficients from its roots.

       (x - 5) (x + 4) (x - 3)  expands to:   x³ -4x² -17x + 60
    """
    # polynomial_from_roots([5, -4, 3]) → [1, -4, -17, 60]
    factors = zip(repeat(1), map(neg, roots))
    return list(reduce(convolve, factors, [1]))

def polynomial_eval(coefficients, x):
    """Evaluate a polynomial at a specific value.

    Computes with better numeric stability than Horner's method.
    """
    # Evaluate x³ -4x² -17x + 60 at x = 5
    # polynomial_eval([1, -4, -17, 60], x=5) → 0
    n = len(coefficients)
    if not n:
        return type(x)(0)
    powers = map(pow, repeat(x), reversed(range(n)))
    return sumprod(coefficients, powers)

def polynomial_derivative(coefficients):
    """Compute the first derivative of a polynomial.

       f(x)  =  x³ -4x² -17x + 60
       f'(x) = 3x² -8x  -17
    """
    # polynomial_derivative([1, -4, -17, 60]) → [3, -8, -17]
    n = len(coefficients)
    powers = reversed(range(1, n))
    return list(map(mul, coefficients, powers))

def sieve(n):
    "Primes less than n."
    # sieve(30) → 2 3 5 7 11 13 17 19 23 29
    if n > 2:
        yield 2
    data = bytearray((0, 1)) * (n // 2)
    for p in iter_index(data, 1, start=3, stop=isqrt(n) + 1):
        data[p*p : n : p+p] = bytes(len(range(p*p, n, p+p)))
    yield from iter_index(data, 1, start=3)

def factor(n):
    "Prime factors of n."
    # factor(99) → 3 3 11
    # factor(1_000_000_000_000_007) → 47 59 360620266859
    # factor(1_000_000_000_000_403) → 1000000000000403
    for prime in sieve(isqrt(n) + 1):
        while not n % prime:
            yield prime
            n //= prime
            if n == 1:
                return
    if n > 1:
        yield n

def is_prime(n):
    "Return True if n is prime."
    # is_prime(1_000_000_000_000_403) → True
    return n > 1 and next(factor(n)) == n

def totient(n):
    "Count of natural numbers up to n that are coprime to n."
    # https://mathworld.wolfram.com/TotientFunction.html
    # totient(12) → 4 because len([1, 5, 7, 11]) == 4
    for prime in set(factor(n)):
        n -= n // prime
    return n