¿Cómo funciona exactamente la comprensión de un generador?

92

¿Qué hace la comprensión del generador? ¿Como funciona? No pude encontrar un tutorial al respecto.

NONEenglisher
fuente
1
Para ser claros, el nombre del lenguaje para estos es expresiones generadoras , no comprensiones generadoras .
ShadowRanger
1
@ShadowRanger Hay una discusión en la lista de correo de Python-dev en julio de 2018 sobre "Sintaxis de comprensión de nombres", donde hubo un acuerdo tentativo pero bastante unánime para llamarlos "comprensiones de generador" en aras de la coherencia.
Aaron Hall

Respuestas:

145

¿Entiende las listas por comprensión? Si es así, una expresión generadora es como una lista de comprensión, pero en lugar de encontrar todos los elementos que le interesan y empaquetarlos en la lista, espera y produce cada elemento de la expresión, uno por uno.

>>> my_list = [1, 3, 5, 9, 2, 6]
>>> filtered_list = [item for item in my_list if item > 3]
>>> print(filtered_list)
[5, 9, 6]
>>> len(filtered_list)
3
>>> # compare to generator expression
... 
>>> filtered_gen = (item for item in my_list if item > 3)
>>> print(filtered_gen)  # notice it's a generator object
<generator object <genexpr> at 0x7f2ad75f89e0>
>>> len(filtered_gen) # So technically, it has no length
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()
>>> # We extract each item out individually. We'll do it manually first.
... 
>>> next(filtered_gen)
5
>>> next(filtered_gen)
9
>>> next(filtered_gen)
6
>>> next(filtered_gen) # Should be all out of items and give an error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> # Yup, the generator is spent. No values for you!
... 
>>> # Let's prove it gives the same results as our list comprehension
... 
>>> filtered_gen = (item for item in my_list if item > 3)
>>> gen_to_list = list(filtered_gen)
>>> print(gen_to_list)
[5, 9, 6]
>>> filtered_list == gen_to_list
True
>>> 

Debido a que una expresión generadora solo tiene que producir un elemento a la vez, puede generar grandes ahorros en el uso de la memoria. Las expresiones generadoras tienen más sentido en escenarios en los que necesita tomar un elemento a la vez, hacer muchos cálculos basados ​​en ese elemento y luego pasar al siguiente elemento. Si necesita más de un valor, también puede usar una expresión generadora y tomar algunos a la vez. Si necesita todos los valores antes de que su programa continúe, use una lista de comprensión en su lugar.

gotgenes
fuente
3
Una pregunta aquí. Usé next (gen_name) para obtener el resultado y funcionó en Python 3. ¿Existe algún escenario específico en el que necesitemos usar __next __ ()?
Ankit Vashistha
3
@AnkitVashistha No, usar siempre en next(...)lugar de .__next__()en Python 3.
Todd Sewell
1
@gotgenes @AnkitVashistha If you need more than one value, you can also use a generator expression and grab a few at a time. ¿Podría dar un ejemplo sobre este uso? Gracias.
LittleZero
20

Una comprensión de generador es la versión perezosa de una comprensión de lista.

Es como una lista de comprensión, excepto que devuelve un iterador en lugar de la lista, es decir, un objeto con un método next () que producirá el siguiente elemento.

Si no está familiarizado con las listas por comprensión, consulte aquí y para los generadores, consulte aquí .

rz.
fuente
4

La comprensión de listas / generadores es una construcción que puede utilizar para crear una nueva lista / generador a partir de una existente.

Digamos que desea generar la lista de cuadrados de cada número del 1 al 10. Puede hacer esto en Python:

>>> [x**2 for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

aquí, range(1,11)genera la lista [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], pero la rangefunción no es un generador antes de Python 3.0 y, por lo tanto, la construcción que he usado es una lista de comprensión.

Si quisiera crear un generador que haga lo mismo, podría hacerlo así:

>>> (x**2 for x in xrange(1,11))
<generator object at 0x7f0a79273488>

En Python 3, sin embargo, rangees un generador, por lo que el resultado depende solo de la sintaxis que use (corchetes o corchetes).

Can Berk Güder
fuente
4
Esto está mal. Si la expresión externa es un generador no tiene nada que ver con si la expresión interna lo es. Aunque obviamente, normalmente no tiene mucho sentido que una expresión generadora tome elementos de una lista, puede hacerlo.
Antimony
¿Se puede reescribir esto con mayor claridad? Entiendo lo que está diciendo, pero como dice Antimony, parece que está diciendo algo más. (y lo que parece que estás diciendo está mal)
Lyndon White
3

La comprensión del generador es una forma sencilla de crear generadores con una determinada estructura. Supongamos que quiere un generatorque genere uno por uno todos los números pares your_list. Si lo crea utilizando el estilo de función, sería así:

def allEvens( L ):
    for number in L:
        if number % 2 is 0:
            yield number

evens = allEvens( yourList )

Puede lograr el mismo resultado con esta expresión de comprensión del generador:

evens = ( number for number in your_list if number % 2 == 0 )

En ambos casos, cuando llama next(evens), obtiene el siguiente número par your_list.

Cristian garcia
fuente
0

La comprensión del generador es un enfoque para crear iterables, algo así como un cursor que se mueve sobre un recurso. Si conoce el cursor mysql o el cursor mongodb, es posible que sepa que todos los datos reales nunca se cargan en la memoria a la vez, sino uno a la vez. Su cursor se mueve hacia adelante y hacia atrás, pero siempre hay un elemento de una fila / lista en la memoria.

En resumen, al usar la comprensión de generadores, puede crear cursores fácilmente en Python.

Muatik
fuente
-1

Otro ejemplo de comprensión del generador:

print 'Generator comprehensions'

def sq_num(n):
    for num in (x**2 for x in range(n)):    
        yield num

for x in sq_num(10):
    print x 
AMIT KUMAR
fuente