iteradores generadores generador codigo python generator

python - generadores - Las expresiones generadoras anidadas se comportan inesperadamente



iteradores python 3 (3)

Creo que esto se debe a que solo se puede iterar sobre el generador una vez. Por lo tanto, después de hacer un bucle completo de e_cd primera vez, esto no producirá nada en otra iteración del ciclo externo.

Con el siguiente código:

A = [1, 2] B = [-2, -1] C = [-1, 2] D = [0, 2] ab = (a + b for a in A for b in B) cd = (c + d for c in C for d in D) abcd = (e_ab + e_cd for e_ab in ab for e_cd in cd)

Se espera que el len(abcd) sea 16 , pero en realidad es 4 . Si en su lugar usé una lista de comprensión, el problema desaparece. ¿Porqué es eso?


Cuando un generador no tiene más valores para devolver, genera una excepción StopIteration . Así es como señalan que han terminado. Dado que no hay una forma integrada de restablecer los generadores, cuando crea un generador de etapas múltiples a partir de generadores, se detendrá en la primera parada que se encuentre en la StopIteration lugar de hacer que los generadores secundarios se StopIteration como sucede con los objetos similares a listas.

itertools.product() puede producir los resultados deseados (repl.it here ):

import itertools A = [1, 2] B = [-2, -1] C = [-1, 2] D = [0, 2] ab = (a + b for a in A for b in B) cd = (c + d for c in C for d in D) abcd = (e_ab + e_cd for e_ab, e_cd in itertools.product(ab,cd))


Solo se puede viajar en el tren del generador una vez, después de que llegue a su destino, no más viajes . En su caso, el generador de cd está agotado y luego no puede repetirse nuevamente.

list objetos de la list , por otro lado, crean un objeto iterador separado cada vez que lo llamas (lo que implícitamente hace el bucle for):

print(iter([1, 2, 3])) # <list_iterator at 0x7f18495d4c88>

y produce un iterador nuevo que puedas usar. Esto sucede cada vez que se invoca iter en él; Dado que cada vez se produce un nuevo objeto, puede ir a través de las listas varias veces. Múltiples paseos!

En resumen, si solo cambia cd para que sea una lista (en general, el objeto que se iterará varias veces):

ab = (a + b for a in A for b in B) cd = [c + d for c in C for d in D] # list-comp instead

producirá el resultado deseado al crear nuevos objetos de iterador desde cd para cada elemento en ab :

abcd = (e_ab + e_cd for e_ab in ab for e_cd in cd) print(len(list(abcd))) # 16

Por supuesto, también puede lograr esto utilizando el product de itertools , pero eso va más allá del punto de por qué sucede esto.