Jak dokładnie działa rozumienie generatora?

96

Co robi generator rozumienia? Jak to działa? Nie mogłem znaleźć samouczka na ten temat.

NONEenglisher
źródło
2
Żeby było jasne, nazwa języka dla nich jest generator wyrażeń , a nie generator listowe .
ShadowRanger
2
@ShadowRanger W lipcu 2018 roku na liście dyskusyjnej Python-dev odbyła się dyskusja na temat „Składni nazewnictwa ze zrozumieniem”, w której była wstępna, ale dość jednomyślna zgoda na nazwanie ich „generatorem zrozumień” w celu zachowania spójności.
Aaron Hall

Odpowiedzi:

149

Czy rozumiesz listy ze zrozumieniem? Jeśli tak, to wyrażenie generatora jest podobne do wyrażenia listowego, ale zamiast znaleźć wszystkie interesujące Cię elementy i spakować je na listę, czeka i zwraca każdy element z wyrażenia, jeden po drugim.

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

Ponieważ wyrażenie generatora musi dawać tylko jeden element naraz, może to prowadzić do dużych oszczędności w zużyciu pamięci. Wyrażenia generatora są najbardziej sensowne w scenariuszach, w których musisz wziąć jeden element na raz, wykonać wiele obliczeń na podstawie tego elementu, a następnie przejść do następnego elementu. Jeśli potrzebujesz więcej niż jednej wartości, możesz również użyć wyrażenia generatora i pobrać kilka na raz. Jeśli potrzebujesz wszystkich wartości przed kontynuowaniem programu, użyj zamiast tego funkcji list.

gotgenes
źródło
4
Jedno pytanie. Użyłem next (gen_name), aby uzyskać wynik i zadziałało w Pythonie 3. Czy istnieje jakiś konkretny scenariusz, w którym musimy użyć __next __ ()?
Ankit Vashistha
4
@AnkitVashistha Nie, zawsze używaj next(...)zamiast .__next__()w Pythonie 3.
Todd Sewell
2
@gotgenes @AnkitVashistha If you need more than one value, you can also use a generator expression and grab a few at a time. Czy mógłbyś podać przykład tego zastosowania? Dzięki.
LittleZero
21

Rozumienie przez generator jest leniwą wersją rozumienia listy.

Jest to podobne do wyrażenia listowego, z tym wyjątkiem, że zwraca iterator zamiast listy, tj. Obiekt z metodą next (), która zwróci następny element.

Jeśli nie jesteś zaznajomiony ze składaniem list, zajrzyj tutaj, a informacje o generatorach - tutaj .

rz.
źródło
4

Rozumienie listy / generatora jest konstrukcją, której można użyć do utworzenia nowej listy / generatora na podstawie istniejącej.

Powiedzmy, że chcesz wygenerować listę kwadratów każdej liczby od 1 do 10. Możesz to zrobić w Pythonie:

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

tutaj range(1,11)generuje listę [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ale rangefunkcja nie jest generatorem przed Pythonem 3.0, dlatego konstrukcja, której użyłem, jest złożeniem listowym.

Gdybym chciał stworzyć generator, który robi to samo, mógłbym to zrobić tak:

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

Jednak w Pythonie 3 rangejest to generator, więc wynik zależy tylko od używanej składni (nawiasy kwadratowe lub nawiasy okrągłe).

Czy Berk Güder
źródło
4
To jest źle. To, czy wyrażenie zewnętrzne jest generatorem, nie ma nic wspólnego z tym, czy wyrażenie wewnętrzne jest. Chociaż oczywiście wyrażenie generatora pobierające elementy z listy zazwyczaj nie ma sensu, możesz to zrobić.
Antymon
Czy można to napisać jaśniej? Rozumiem, co mówisz, ale jak mówi Antymon, wygląda na to, że mówisz coś innego. (i wygląda na to, że mówisz źle)
Lyndon White,
4

Rozumienie przez generator to łatwy sposób na tworzenie generatorów o określonej strukturze. Powiedzmy, że chcesz, generatoraby wypisywano po kolei wszystkie liczby parzyste your_list. Jeśli utworzysz go za pomocą stylu funkcji, wyglądałoby to tak:

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

evens = allEvens( yourList )

Możesz osiągnąć ten sam wynik za pomocą tego wyrażenia rozumienia generatora:

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

W obu przypadkach dzwoniąc next(evens)otrzymasz następny parzysty numer your_list.

Cristian Garcia
źródło
1

Zrozumienie generatora to podejście do tworzenia elementów iteracyjnych, coś w rodzaju kursora poruszającego się po zasobie. Jeśli znasz kursor mysql lub kursor mongodb, możesz być świadomy, że całe rzeczywiste dane nigdy nie są ładowane do pamięci naraz, ale pojedynczo. Kursor porusza się tam iz powrotem, ale w pamięci zawsze znajduje się jeden wiersz / element listy.

Krótko mówiąc, korzystając ze zrozumienia generatorów, możesz łatwo tworzyć kursory w Pythonie.

Muatik
źródło
0

Inny przykład zrozumienia przez Generator:

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
źródło
0

Generatory są takie same jak tylko listy, niewielka różnica polega na tym, że na listach wszystkie wymagane liczby lub pozycje z listy uzyskujemy po jedynkach, ale w generatorach wymagane liczby są podawane pojedynczo. Aby uzyskać wymagane przedmioty, musimy użyć pętli for, aby uzyskać wszystkie wymagane przedmioty.

#to get all the even numbers in given range
 
def allevens(n):
    for x in range(2,n):
        if x%2==0:
            yield x

for x in allevens(10)
print(x)

#output
2
4
6
8
Onkar Raut
źródło