iter has generadores attribute python generator iterable

python - has - Longitud de salida del generador



python iter (8)

Esto es un truco, pero si realmente quieres tener un trabajo lento en un iterable general (consumiéndolo en el camino), puedes crear tu propia versión de len .

La función len es esencialmente equivalente a la siguiente (aunque las implementaciones suelen proporcionar algunas optimizaciones para evitar la búsqueda adicional):

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

Por lo tanto, podemos definir nuestro new_len para probar eso, y si __len__ no existe, cuente el número de elementos consumiendo el iterable:

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

Lo anterior funciona en Python 2/3, y (hasta donde yo sé) debería cubrir todo tipo de iterable concebible.

Python proporciona un buen método para obtener la longitud de un iterable ansioso, len(x) que es. Pero no pude encontrar nada similar para los iterables perezosos representados por las funciones y las funciones del generador. Por supuesto, no es difícil escribir algo como:

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

Pero no puedo deshacerme de la sensación de que estoy volviendo a implementar una bicicleta.

(Mientras estaba escribiendo la función, un pensamiento me vino a la mente: tal vez realmente no exista tal función, porque "destruye" su argumento. Sin embargo, no es un problema para mi caso).

PD: con respecto a las primeras respuestas, sí, algo como len(list(x)) también funcionaría, pero eso aumenta drásticamente el uso de la memoria.

PPS: revisado ... Ignore el PS, parece que cometí un error al intentarlo, funciona bien. Lo siento por la molestia.


La forma más fácil es probablemente solo sum(1 for _ in gen) donde gen es tu generador.


No hay uno porque no puedes hacerlo en el caso general, ¿y si tienes un generador infinito perezoso? Por ejemplo:

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

Esto nunca termina pero generará los números de Fibonacci. Puede obtener tantos números de Fibonacci como desee llamando al next() .

Si realmente necesita saber la cantidad de elementos que existen, entonces no puede iterar linealmente en ellos una vez, así que simplemente use una estructura de datos diferente, como una lista normal.


Por definición, solo un subconjunto de generadores regresará después de un cierto número de argumentos (tienen una longitud predefinida), e incluso entonces, solo un subconjunto de estos generadores finitos tiene un final predecible (el acceso al generador puede tener efectos secundarios que podría detener el generador antes).

Si desea implementar métodos de longitud para su generador, primero debe definir lo que considera la "longitud" (¿es la cantidad total de elementos? ¿La cantidad de elementos restantes?), Luego ajuste su generador en una clase. Aquí hay un ejemplo:

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

Aquí es cómo usarlo:

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]


Pruebe el paquete more_itertools para una solución simple. Ejemplo:

>>> import more_itertools >>> it = iter("abcde") # sample generator >>> it <str_iterator at 0x4ab3630> >>> more_itertools.ilen(it) 5

Vea esta publicación para otro ejemplo aplicado.


Puede usar enumerate () para recorrer el flujo de datos generado, luego devolver el último número: el número de elementos.

Traté de usar itertools.count () con itertools.izip () pero no tuve suerte. Esta es la mejor / más corta respuesta que he encontrado:

#!/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

La solución de Kamil Kisiel es mucho mejor:

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



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

O mejor aún:

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

Si no es iterable, arrojará un TypeError .

O bien, si desea contar algo específico en el generador:

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)