222 Stimmen

Anzahl der Elemente in einem Iterator in Python ermitteln

Gibt es einen effizienten Weg, um zu wissen, wie viele Elemente in einem Iterator in Python, im Allgemeinen, ohne Iteration durch jedes und Zählen sind?

0 Stimmen

323voto

John Howard Punkte 56237

Dieser Code sollte funktionieren:

>>> iter = (i for i in range(50))
>>> sum(1 for _ in iter)
50

Obwohl es jedes Element durchläuft und zählt, ist dies der schnellste Weg, dies zu tun.

Es funktioniert auch, wenn der Iterator kein Element hat:

>>> sum(1 for _ in range(0))
0

Natürlich läuft es für eine unendliche Eingabe ewig, also denken Sie daran, dass Iteratoren unendlich sein können:

>>> sum(1 for _ in itertools.count())
[nothing happens, forever]

Beachten Sie auch, dass der Iterator wird erschöpft sein und bei weiteren Versuchen, sie zu nutzen, werden keine Elemente . Das ist eine unvermeidliche Folge des Iterator-Designs von Python. Wenn Sie die Elemente behalten wollen, müssen Sie sie in einer Liste oder ähnlichem speichern.

29 Stimmen

Sieht für mich aus wie dies tut genau das, was OP nicht tun will: Iterieren durch den Iterator und zählen.

47 Stimmen

Dies ist eine platzsparende Methode zum Zählen der Elemente in einer Iterablen

13 Stimmen

Dies ist zwar nicht das, was OP will, da seine Frage keine Antwort hat, aber diese Antwort vermeidet die Instanziierung einer Liste und ist erfahrungsgemäß um eine Konstante schneller als die oben aufgeführte reduce-Methode.

141voto

Tomasz Wysocki Punkte 10546

Nein, das ist nicht möglich.

Beispiel:

import random

def gen(n):
    for i in xrange(n):
        if random.randint(0, 1) == 0:
            yield i

iterator = gen(10)

Länge der iterator ist unbekannt, bis man sie durchläuft.

18 Stimmen

Alternativ dazu, def gen(): yield random.randint(0, 1) ist unendlich, so dass man niemals eine Länge finden kann, indem man sie durchläuft.

2 Stimmen

Um das Offensichtliche zu bestätigen: Der beste Weg, die "Größe" eines Iterators zu ermitteln, besteht darin, die Anzahl der Iterationen zu zählen, richtig? In diesem Fall wäre es numIters = 0 ; while iterator: numIters +=1 ?

0 Stimmen

Interessant, es geht also um das Halteproblem

100voto

Daenyth Punkte 33328

Nein, bei jeder Methode müssen Sie jedes Ergebnis auflösen. Sie können

iter_length = len(list(iterable))

aber wenn man das auf einen unendlichen Iterator anwendet, kommt man natürlich nie zurück. Es wird auch den Iterator verbrauchen und es muss zurückgesetzt werden, wenn Sie den Inhalt verwenden möchten.

Wenn Sie uns sagen, welches Problem Sie wirklich lösen wollen, können wir vielleicht einen besseren Weg finden, um Ihr eigentliches Ziel zu erreichen.

Bearbeiten: Mit list() wird die gesamte Iterable auf einmal in den Speicher eingelesen, was unerwünscht sein kann. Eine andere Möglichkeit ist, die

sum(1 for _ in iterable)

wie eine andere Person gepostet hat. So wird vermieden, dass es im Gedächtnis bleibt.

0 Stimmen

Das Problem ist, dass ich eine Datei mit "pysam" lese, die Millionen von Einträgen enthält. Pysam gibt einen Iterator zurück. Um eine bestimmte Menge zu berechnen, muss ich wissen, wie viele Lesevorgänge in der Datei sind, aber ich muss nicht jeden einzelnen lesen... das ist das Problem.

9 Stimmen

Ich bin kein Pysam-Benutzer, aber wahrscheinlich ist es eine "faule" Lesedatei. Das macht Sinn, weil man keine große Datei im Speicher haben will. Wenn Sie also die Anzahl der Datensätze vor der Iteration wissen müssen, ist die einzige Möglichkeit, zwei Iteratoren zu erstellen, und den ersten zum Zählen der Elemente und den zweiten zum Lesen der Datei zu verwenden. ÜBRIGENS. Verwenden Sie nicht len(list(iterable)) werden alle Daten in den Speicher geladen. Sie können verwenden: reduce(lambda x, _: x+1, iterable, 0) . Bearbeiten: Zonda333 Code mit Summe ist auch gut.

1 Stimmen

@user248237: Warum sagen Sie, dass Sie wissen müssen, wie viele Einträge vorhanden sind, um eine bestimmte Menge zu berechnen? Sie könnten einfach eine feste Anzahl von ihnen lesen und den Fall verwalten, wenn es weniger als diese feste Anzahl gibt (wirklich einfach zu tun mit iterslice). Gibt es einen anderen Grund, warum Sie alle Einträge lesen müssen?

49voto

zuo Punkte 431

Das geht nicht (es sei denn, der Typ eines bestimmten Iterators implementiert einige spezifische Methoden, die dies ermöglichen).

Im Allgemeinen können Sie Iterator-Elemente nur zählen, indem Sie den Iterator konsumieren. Das ist wahrscheinlich eine der effizientesten Methoden:

import itertools
from collections import deque

def count_iter_items(iterable):
    """
    Consume an iterable not reading it into memory; return the number of items.
    """
    counter = itertools.count()
    deque(itertools.izip(iterable, counter), maxlen=0)  # (consume at C speed)
    return next(counter)

(Für Python 3.x ersetzen Sie itertools.izip con zip ).

4 Stimmen

+1: in einem Zeitvergleich mit sum(1 for _ in iterator) Das war fast doppelt so schnell.

1 Stimmen

I

0 Stimmen

Sehr schöne Antwort. Ich würde ein Kopfgeld darauf aussetzen.

20voto

badp Punkte 11166

Irgendwie schon. Sie könnte überprüfen Sie die __length_hint__ Methode, aber seien Sie gewarnt, dass es sich (zumindest bis Python 3.4, wie gsnedders hilfreich anmerkt) um eine undokumentiertes Detail der Implementierung ( folgende Nachricht im Thema ), die sehr wohl verschwinden oder stattdessen nasale Dämonen beschwören können.

Sonst nicht. Iteratoren sind einfach ein Objekt, das nur die next() Methode. Sie können sie so oft wie nötig aufrufen, und es kann sein, dass sie am Ende erhöhen oder nicht StopIteration . Glücklicherweise ist dieses Verhalten in den meisten Fällen für den Programmierer transparent :)

5 Stimmen

Dies ist nicht mehr der Fall, seit PEP 424 und Python 3.4. __length_hint__ ist jetzt dokumentiert, aber es ist ein Hinweis und übernimmt keine Garantie für die Richtigkeit.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X