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

14voto

Michael Punkte 6665

Ein kurzes Benchmarking:

import collections
import itertools

def count_iter_items(iterable):
    counter = itertools.count()
    collections.deque(itertools.izip(iterable, counter), maxlen=0)
    return next(counter)

def count_lencheck(iterable):
    if hasattr(iterable, '__len__'):
        return len(iterable)

    d = collections.deque(enumerate(iterable, 1), maxlen=1)
    return d[0][0] if d else 0

def count_sum(iterable):           
    return sum(1 for _ in iterable)

iter = lambda y: (x for x in xrange(y))

%timeit count_iter_items(iter(1000))
%timeit count_lencheck(iter(1000))
%timeit count_sum(iter(1000))

Die Ergebnisse:

10000 loops, best of 3: 37.2 µs per loop
10000 loops, best of 3: 47.6 µs per loop
10000 loops, best of 3: 61 µs per loop

D.h. die einfache count_iter_items ist der Weg zu gehen.

Anpassung an python3:

61.9 µs ± 275 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
74.4 µs ± 190 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
82.6 µs ± 164 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

0 Stimmen

Hinweis: Dieser Test basiert auf python2

14voto

Alex-Bogdanov Punkte 1704

Für diejenigen, die sich für die Zusammenfassung dieser Diskussion interessieren. Die endgültigen Spitzenwerte für die Zählung eines 50 Millionen langen Generatorausdrucks mit:

  • len(list(gen)) ,
  • len([_ for _ in gen]) ,
  • sum(1 for _ in gen),
  • ilen(gen) (aus mehr_itertool ),
  • reduce(lambda c, i: c + 1, gen, 0) ,

sortiert nach Ausführungsleistung (einschließlich Speicherverbrauch), wird Sie überraschen:

```

1: test_list.py:8: 0.492 KiB

gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))

('list, sec', 1.9684218849870376)

2: test_list_compr.py:8: 0.867 KiB

gen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])

('list_compr, sec', 2.5885991149989422)

3: test_sum.py:8: 0.859 KiB

gen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()

('Summe, Sekunde', 3,441088170016883)

4: more_itertools/more.py:413: 1.266 KiB

d = deque(enumerate(iterable, 1), maxlen=1)

test_ilen.py:10: 0.875 KiB
gen = (i for i in data*1000); t0 = monotonic(); ilen(gen)

('ilen, sec', 9.812256851990242)

5: test_reduce.py:8: 0.859 KiB

gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)

('reduce, sec', 13.436614598002052) ```

Also, len(list(gen)) ist der häufigste und am wenigsten speicherintensive

2 Stimmen

Wie haben Sie den Speicherverbrauch gemessen?

3 Stimmen

Können Sie erklären, warum len(list(gen)) sollte weniger Speicher verbrauchen als der Ansatz, der auf reduce? Ersterer erzeugt eine neue list die die Zuweisung von Speicherplatz beinhaltet, während letzteres nicht der Fall sein sollte. Ich würde also erwarten, dass letztere speichereffizienter ist. Außerdem hängt der Speicherverbrauch vom Elementtyp ab.

0 Stimmen

FYI: Ich kann für Python 3.6.8 (auf einem MacBookPro) reproduzieren, dass Methode 1 die anderen Methoden in Bezug auf die Laufzeit übertrifft (ich übersprungen Methode 4).

11voto

Erwin Mayer Punkte 16992

Ich mag die Kardinalität Es ist sehr leichtgewichtig und versucht, die schnellstmögliche Implementierung zu verwenden, die je nach Iterable verfügbar ist.

使用方法

>>> import cardinality
>>> cardinality.count([1, 2, 3])
3
>>> cardinality.count(i for i in range(500))
500
>>> def gen():
...     yield 'hello'
...     yield 'world'
>>> cardinality.count(gen())
2

Der eigentliche count() Die Umsetzung ist wie folgt:

def count(iterable):
    if hasattr(iterable, '__len__'):
        return len(iterable)

    d = collections.deque(enumerate(iterable, 1), maxlen=1)
    return d[0][0] if d else 0

0 Stimmen

Ich nehme an, Sie können immer noch über den Iterator iterieren, wenn Sie diese Funktion verwenden, ja?

0 Stimmen

@jcollum Wenn ich mir den Code ansehe, der für count am Ende dieser Antwort wird die Iterable verbraucht, wenn sie keine .__len__ Attribut. Wenn es sich um ein "Einweg"-Objekt wie einen Generator handelt, dann ist es nach dem Aufruf von count darauf.

9voto

Jesus Ramos Punkte 22582

Ein Iterator ist nur ein Objekt, das einen Zeiger auf das nächste Objekt hat, das von einer Art Puffer oder Stream gelesen werden soll, es ist wie eine LinkedList, bei der man nicht weiß, wie viele Einträge man hat, bis man sie durchläuft. Iteratoren sollen effizient sein, weil sie nur über Referenzen mitteilen, was als Nächstes kommt, anstatt eine Indexierung zu verwenden (aber wie Sie gesehen haben, verlieren Sie die Möglichkeit zu sehen, wie viele Einträge als Nächstes kommen).

2 Stimmen

Ein Iterator ist nichts anderes als eine verknüpfte Liste. Ein Objekt, das von einem Iterator zurückgegeben wird, verweist nicht auf das nächste Objekt, und diese Objekte werden nicht (notwendigerweise) im Speicher abgelegt. Vielmehr kann er ein Objekt nach dem anderen auf der Grundlage einer beliebigen inneren Logik zurückgeben (die auf einer gespeicherten Liste basieren kann, aber nicht muss).

1 Stimmen

@Tom Ich war mit LinkedList als ein Beispiel vor allem in, dass Sie nicht wissen, wie viel Sie haben, da Sie nur wissen, was als nächstes in gewissem Sinne (wenn es etwas gibt). Ich entschuldige mich, wenn meine Formulierung ein wenig daneben ist oder wenn ich angedeutet habe, dass sie ein und dasselbe sind.

8voto

Kevin Jacobs Punkte 616

In Bezug auf Ihre ursprüngliche Frage ist die Antwort immer noch, dass es im Allgemeinen keine Möglichkeit gibt, die Länge eines Iterators in Python zu kennen.

Da Ihre Frage durch eine Anwendung der pysam-Bibliothek motiviert ist, kann ich eine spezifischere Antwort geben: Ich arbeite an PySAM mit, und die endgültige Antwort lautet, dass SAM/BAM-Dateien keine genaue Anzahl der ausgerichteten Reads liefern. Auch aus einer BAM-Indexdatei ist diese Information nicht ohne Weiteres verfügbar. Das Beste, was man tun kann, ist, die ungefähre Anzahl der Alignments zu schätzen, indem man die Position des Dateizeigers nach dem Lesen einer Reihe von Alignments verwendet und auf der Grundlage der Gesamtgröße der Datei extrapoliert. Dies ist ausreichend, um einen Fortschrittsbalken zu implementieren, aber keine Methode, um Alignments in konstanter Zeit zu zählen.

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