Como funciona exatamente a compreensão do gerador?

91

O que a compreensão do gerador faz? Como funciona? Não consegui encontrar um tutorial sobre isso.

NONEenglisher
fonte
1
Para ser claro, o nome da linguagem para esses é expressões geradoras , não compreensões geradoras .
ShadowRanger
1
@ShadowRanger Há uma discussão na lista de discussão Python-dev em julho de 2018 sobre "Sintaxe de compreensão de nomenclatura" onde houve um acordo provisório, mas bastante unânime, para chamá-los de "compreensão de gerador" por uma questão de consistência.
Aaron Hall

Respostas:

144

Você entende as compreensões de lista? Nesse caso, uma expressão geradora é como uma compreensão de lista, mas em vez de encontrar todos os itens de seu interesse e colocá-los na lista, ela espera e produz cada item da expressão, um por um.

>>> 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
>>> 

Como uma expressão geradora só precisa produzir um item por vez, pode levar a uma grande economia no uso de memória. As expressões do gerador fazem mais sentido em cenários em que você precisa pegar um item por vez, fazer muitos cálculos com base nesse item e, em seguida, passar para o próximo item. Se precisar de mais de um valor, você também pode usar uma expressão geradora e obter alguns de cada vez. Se você precisar de todos os valores antes de prosseguir com o programa, use uma compreensão de lista.

Gotgenes
fonte
3
Uma pergunta aqui. Usei next (gen_name) para obter o resultado e funcionou no Python 3. Existe algum cenário específico em que precisamos usar __next __ ()?
Ankit Vashistha
2
@AnkitVashistha Não, sempre use em next(...)vez de .__next__()no Python 3.
Todd Sewell
@gotgenes @AnkitVashistha If you need more than one value, you can also use a generator expression and grab a few at a time. Você poderia dar um exemplo sobre esse uso? Obrigado.
LittleZero
19

Uma compreensão de gerador é a versão preguiçosa de uma compreensão de lista.

É como uma compreensão de lista, exceto que retorna um iterador em vez da lista, ou seja, um objeto com um método next () que produzirá o próximo elemento.

Se você não estiver familiarizado com compreensões lista ver aqui e para os geradores de ver aqui .

rz.
fonte
4

A compreensão de lista / gerador é uma construção que você pode usar para criar uma nova lista / gerador a partir de uma existente.

Digamos que você queira gerar a lista de quadrados de cada número de 1 a 10. Você pode fazer isso em Python:

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

aqui, range(1,11)gera a lista [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], mas a rangefunção não é um gerador anterior ao Python 3.0 e, portanto, a construção que usei é uma compreensão de lista.

Se eu quisesse criar um gerador que fizesse a mesma coisa, poderia fazer assim:

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

No Python 3, no entanto, rangeé um gerador, então o resultado depende apenas da sintaxe que você usa (colchetes ou colchetes).

Can Berk Güder
fonte
4
Isto está errado. O fato de a expressão externa ser um gerador não tem nada a ver com o fato de a expressão interna ser. Embora, obviamente, normalmente não haja muito sentido em uma expressão de gerador pegar elementos de uma lista, você pode fazer isso.
Antimônio
Isso pode ser reescrito com mais clareza? Eu entendo o que você está dizendo, mas como Antimony diz, parece que você está dizendo outra coisa. (e parece que você está dizendo que está errado)
Lyndon White
3

A compreensão do gerador é uma maneira fácil de criar geradores com uma determinada estrutura. Digamos que você queira um generatorque produza um por um todos os números pares your_list. Se você criá-lo usando o estilo de função, será assim:

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

evens = allEvens( yourList )

Você pode obter o mesmo resultado com esta expressão de compreensão do gerador:

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

Em ambos os casos, quando você liga, next(evens)obtém o próximo número par your_list.

Cristian garcia
fonte
0

A compreensão do gerador é uma abordagem para criar iteráveis, algo como um cursor que se move sobre um recurso. Se você conhece o cursor mysql ou cursor mongodb, você deve estar ciente de que todos os dados reais nunca são carregados na memória de uma vez, mas um de cada vez. Seu cursor se move para frente e para trás, mas sempre há um elemento de uma linha / lista na memória.

Resumindo, usando a compreensão de geradores, você pode facilmente criar cursores em python.

Muatik
fonte
-1

Outro exemplo de compreensão do gerador:

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
fonte