Come funziona esattamente una comprensione del generatore?


92

Cosa fa la comprensione del generatore? Come funziona? Non sono riuscito a trovare un tutorial su di esso.


1
Per essere chiari, il nome della lingua per questi è espressioni del generatore , non comprensioni del generatore .
ShadowRanger

1
@ShadowRanger C'è una discussione sulla mailing list di Python-dev nel luglio del 2018 sulla "Sintassi della comprensione dei nomi" dove c'era un accordo provvisorio ma abbastanza unanime per chiamarli "comprensione del generatore" per motivi di coerenza.
Aaron Hall

Risposte:


145

Capisci la comprensione delle liste? In tal caso, un'espressione generatore è come una comprensione di una lista, ma invece di trovare tutti gli elementi che ti interessano e comprimerli in una lista, attende e restituisce ogni elemento dall'espressione, uno per 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
>>> 

Poiché un'espressione del generatore deve produrre solo un elemento alla volta, può portare a grandi risparmi nell'utilizzo della memoria. Le espressioni del generatore hanno più senso negli scenari in cui è necessario prendere un elemento alla volta, eseguire molti calcoli basati su quell'elemento e quindi passare all'elemento successivo. Se hai bisogno di più di un valore, puoi anche usare un'espressione del generatore e prenderne alcuni alla volta. Se hai bisogno di tutti i valori prima che il tuo programma proceda, usa invece una comprensione dell'elenco.


3
Una domanda qui. Ho usato next (gen_name) per ottenere il risultato e ha funzionato in Python 3. C'è qualche scenario specifico in cui dobbiamo usare __next __ ()?
Ankit Vashistha

3
@AnkitVashistha No, usa sempre next(...)invece che .__next__()in 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. Potrebbe fornire un esempio su questo utilizzo? Grazie.
LittleZero

20

Una comprensione del generatore è la versione pigra di una comprensione dell'elenco.

È proprio come una comprensione di lista tranne per il fatto che restituisce un iteratore invece della lista, cioè un oggetto con un metodo next () che produrrà l'elemento successivo.

Se non hai familiarità con la comprensione delle liste, vedi qui e per i generatori vedi qui .


4

La comprensione della lista / generatore è un costrutto che puoi usare per creare una nuova lista / generatore da uno esistente.

Supponiamo che tu voglia generare l'elenco dei quadrati di ogni numero da 1 a 10. Puoi farlo in Python:

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

qui, range(1,11)genera la lista [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ma la rangefunzione non è un generatore prima di Python 3.0, e quindi il costrutto che ho usato è una comprensione della lista.

Se volessi creare un generatore che faccia la stessa cosa, potrei farlo in questo modo:

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

In Python 3, invece, rangeè un generatore, quindi il risultato dipende solo dalla sintassi che utilizzi (parentesi quadre o parentesi tonde).


4
Questo è sbagliato. Se l'espressione esterna è un generatore non ha nulla a che fare con se l'espressione interna lo è. Sebbene ovviamente, di solito non abbia molto senso in un'espressione del generatore che prenda elementi da un elenco, puoi farlo.
Antimonio

Può essere riscritto più chiaramente? Capisco quello che stai dicendo, ma come dice Antimony, sembra che tu stia dicendo qualcos'altro. (e la cosa che sembra che tu stia dicendo è sbagliata)
Lyndon White

3

La comprensione del generatore è un modo semplice per creare generatori con una certa struttura. Diciamo che vuoi un generatorche emetta uno per uno tutti i numeri pari in your_list. Se lo crei usando lo stile della funzione sarebbe come questo:

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

evens = allEvens( yourList )

Potresti ottenere lo stesso risultato con questa espressione di comprensione del generatore:

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

In entrambi i casi, quando chiami next(evens)ricevi il successivo numero pari your_list.


0

La comprensione del generatore è un approccio per creare iterabili, qualcosa come un cursore che si muove su una risorsa. Se conosci il cursore mysql o il cursore mongodb, potresti essere consapevole del fatto che tutti i dati effettivi non vengono mai caricati nella memoria contemporaneamente, ma uno alla volta. Il cursore si sposta avanti e indietro, ma in memoria è sempre presente un elemento di una riga / elenco.

In breve, utilizzando la comprensione dei generatori puoi facilmente creare cursori in Python.


-1

Un altro esempio di comprensione del generatore:

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 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.