Frage

    

Diese Frage bereits eine Antwort hier:

         

Python bietet eine schöne Methode für das Erhalten Länge eines eifrigen iterable, len(x) das ist. Aber ich kann nichts Vergleichbares für faules Iterables durch den Generator Comprehensions und Funktionen dargestellt finden. Natürlich ist es nicht schwer, so etwas wie zu schreiben:

def iterlen(x):
  n = 0
  try:
    while True:
      next(x)
      n += 1
  except StopIteration: pass
  return n

Aber ich kann nicht von einem Gefühl loswerden, dass ich ein Fahrrad bin Neuimplementierung.

(Während ich die Funktion tippte, ein Gedanke kam meiner Meinung nach: vielleicht gibt es wirklich keine solche Funktion, weil sie „zerstört“ sein Argument nicht ein Problem für meinen Fall, though.)

.

P. S .: über die ersten Antworten - ja, so etwas wie len(list(x)) auch funktionieren würde, aber das erhöht drastisch die Nutzung von Speichern

.

P.P.S .: nachgeprüften ... Ignorieren Sie die P. S., scheint, dass ich einen Fehler gemacht, während das versucht, es funktioniert gut. Sorry für die Mühe.

War es hilfreich?

Lösung

Es gibt nicht ein, weil man es im allgemeinen Fall nicht tun können - was, wenn Sie einen fauler unendlich Generator? Zum Beispiel:

def fib():
    a, b = 0, 1
    while True:
        a, b = b, a + b
        yield a

Damit endet nie aber wird die Fibonacci-Zahlen erzeugen. Sie können so viele Fibonacci-Zahlen erhalten, wie Sie durch den Aufruf next() wollen.

Wenn Sie wirklich die Anzahl der Elemente müssen wissen, es gibt, dann kann man nicht durch sie durchlaufen linear einmal sowieso, also nur eine andere Datenstruktur verwenden, wie zum Beispiel eine regelmäßige Liste.

Andere Tipps

Der einfachste Weg ist wahrscheinlich nur sum(1 for _ in gen) wo gen Ihr Generator ist.

def count(iter):
    return sum(1 for _ in iter)

Oder noch besser:

def count(iter):
    try:
        return len(iter)
    except TypeError:
        return sum(1 for _ in iter)

Wenn es nicht durchsuchbar ist, wird es eine TypeError werfen.

Oder, wenn Sie bestimmte im Generator etwas zählen:

def count(iter, key=None):
    if key:
        if callable(key):
            return sum(bool(key(x)) for x in iter)
        return sum(x == key for x in iter)
    try:
        return len(iter)
    except TypeError:
        return sum(1 for _ in iter)

Also, für diejenigen, die die Zusammenfassung dieser Diskussion wissen möchten. Die endgültigen Top-Noten für einen 50 Millionen-lengthed Generator Ausdruck Zählen mit:

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

nach Leistung der Ausführung sortiert (einschließlich Speicherverbrauch), werden Sie überrascht:

`` `

1: test_list.py:8: 0.492 KiB

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

(Liste, s ', 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, s', 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, s', 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, s', 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)

( 'reduzieren, s', 13,436614598002052) `` `

Also, len(list(gen)) ist die häufigste und weniger Speicher Verbrauch

Sie enumerate () in einer Schleife durch den erzeugten Datenstrom verwenden können, dann die letzte Nummer zurück -. Die Anzahl der Elemente

Ich habe versucht, itertools.count () mit itertools.izip (), aber ohne Glück zu verwenden. Dies ist die beste / kürzeste Antwort, die ich gekommen bin oben mit:

#!/usr/bin/python

import itertools

def func():
    for i in 'yummy beer':
        yield i

def icount(ifunc):
    size = -1 # for the case of an empty iterator
    for size, _ in enumerate(ifunc()):
        pass
    return size + 1

print list(func())
print 'icount', icount(func)

# ['y', 'u', 'm', 'm', 'y', ' ', 'b', 'e', 'e', 'r']
# icount 10

Kamil Kisiel-Lösung ist viel besser:

def count_iterable(i):
    return sum(1 for e in i)

Verwenden Sie reduzieren (Funktion, iterable [, initializer]) eine speichereffiziente rein funktionale Lösung:

>>> iter = "This string has 30 characters."
>>> reduce(lambda acc, e: acc + 1, iter, 0)
30

Per Definition nur eine Teilmenge von Generatoren wird nach einer bestimmten Anzahl von Argumenten zurück (eine vordefinierte Länge), und selbst dann nur eine Teilmenge dieser endlichen Generatoren haben ein vorhersagbares Ende (den Generator Zugriff Seite haben kann -Effekte, die den Generator früher aufhören können).

Wenn Sie möchten Länge Methoden für Ihren Generator implementieren, müssen Sie zunächst definieren, was Sie die „Länge“ betrachten (es ist die Gesamtzahl der Elemente? Die Anzahl der verbleibenden Elemente?), Dann wickeln Sie Ihren Generator in einer Klasse . Hier ein Beispiel:

class MyFib(object):
    """
    A class iterator that iterates through values of the
    Fibonacci sequence, until, optionally, a maximum length is reached.
    """

    def __init__(self, length):
        self._length = length
        self._i = 0

     def __iter__(self):
        a, b = 0, 1
        while not self._length or self._i < self._length:
            a, b = b, a + b
            self._i += 1
            yield a

    def __len__(self):
        "This method returns the total number of elements"
        if self._length:
            return self._length
        else:
            raise NotImplementedError("Infinite sequence has no length")
            # or simply return None / 0 depending
            # on implementation

Hier ist, wie es zu benutzen:

In [151]: mf = MyFib(20)

In [152]: len(mf)
Out[152]: 20

In [153]: l = [n for n in mf]

In [154]: len(l)
Out[154]: 20

In [155]: l
Out[155]: 
[1,
 1,
 2,
...
6765]


In [156]: mf0 = MyFib(0)

In [157]: len(mf0)
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-157-2e89b32ad3e4> in <module>()
----> 1 len(mf0)

/tmp/ipython_edit_TWcV1I.py in __len__(self)
     22             return self._length
     23         else:
---> 24             raise NotImplementedError
     25             # or simply return None / 0 depending
     26             # on implementation

NotImplementedError: 

In [158]: g = iter(mf0)

In [159]: l0 = [g.next(), g.next(), g.next()]

In [160]: l0
Out[160]: [1, 1, 2]

Versuchen Sie, die more_itertools Paket für eine einfache Lösung. Beispiel:

>>> import more_itertools

>>> it = iter("abcde")                                         # sample generator
>>> it
<str_iterator at 0x4ab3630>

>>> more_itertools.ilen(it)
5

Siehe diesen Beitrag für ein weiteres Beispiel angewendet wird.

Dies ist ein Hack, aber wenn Sie wirklich len Arbeit haben sich auf eine allgemeine iterable (raubend es in der Art und Weise) möchten, können Sie Ihre eigene Version von len erstellen.

Die len Funktion entspricht im Wesentlichen die folgenden (obwohl Implementierungen in der Regel einige Optimierungen bieten die zusätzliche Lookup zu vermeiden):

def len(iterable):
    return iterable.__len__()

Deshalb können wir unsere new_len definieren, das zu versuchen, und wenn __len__ nicht vorhanden ist, die Anzahl der Elemente selbst zählen durch den Verzehr der durchsuchbar:

def new_len(iterable):
    try:
      return iterable.__len__()
    except AttributeError:
      return sum(1 for _ in iterable)

Die oben genannten Arbeiten in Python 3.2 und (soweit ich weiß) soll jede erdenkliche Art von iterable abdecken.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top