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.