Як саме працює розуміння генератора?


91

Що робить розуміння генератора? Як це працює? Я не міг знайти підручник про це.


1
Щоб бути зрозумілим, назва мови - це вирази генератора , а не розуміння генератора .
ShadowRanger

1
@ShadowRanger У липні 2018 року в списку розсилки Python-dev йде дискусія щодо "Синтаксису розуміння імен", де була попередня, але досить одностайна згода назвати їх "генераторами розумінь" заради узгодженості.
Зал Аарона

Відповіді:


144

Ви розумієте розуміння списку? Якщо так, вираз генератора схожий на розуміння списку, але замість того, щоб знайти всі цікавлять вас елементи та упакувати їх у список, він чекає і видає кожен елемент із виразу по одному.

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

Оскільки генератор виразу повинен видавати лише один елемент за раз, це може призвести до значної економії використання пам'яті. Вирази генератора мають найбільший сенс у сценаріях, коли потрібно брати по одному елементу за раз, робити багато обчислень на основі цього елемента, а потім переходити до наступного елемента. Якщо вам потрібно більше одного значення, ви також можете скористатися виразом генератора та взяти кілька за раз. Якщо вам потрібні всі значення до того, як ваша програма продовжить, використовуйте натомість розуміння списку.


3
Одне питання тут. Я використовував next (gen_name) для отримання результату, і він працював у Python 3. Чи існує якийсь конкретний сценарій, коли нам потрібно використовувати __next __ ()?
Ankit Vashistha

2
@AnkitVashistha Ні, завжди використовувати next(...)замість .__next__()у Python 3.
Тодд Сьюелл

@gotgenes @AnkitVashistha If you need more than one value, you can also use a generator expression and grab a few at a time. Не могли б ви навести приклад про це використання? Дякую.
LittleZero

19

Розуміння генератора - це лінива версія розуміння списку.

Це подібно до розуміння списку, за винятком того, що він повертає ітератор замість списку, тобто об'єкт із методом next (), який дасть наступний елемент.

Якщо ви не знайомі з розумінням списків, дивіться тут, а щодо генераторів - тут .


4

Розуміння списку / генератора - це конструкція, за допомогою якої ви можете створити новий список / генератор із існуючого.

Скажімо, ви хочете сформувати список квадратів кожного числа від 1 до 10. Ви можете зробити це в Python:

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

тут range(1,11)генерує список [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], але rangeфункція не є генератором до Python 3.0, а тому побудована мною конструкція є розумінням списку.

Якби я хотів створити генератор, який робить те саме, я міг би зробити це так:

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

Однак у Python 3 rangeє генератором, тому результат залежить лише від використовуваного вами синтаксису (квадратні дужки або круглі дужки).


4
Це неправильно. Чи є зовнішній вираз генератором, не має нічого спільного з тим, чи є внутрішній вираз. Хоча очевидно, зазвичай у виразі генератора немає великого сенсу брати елементи зі списку, ви можете це зробити.
Сурма

Чи можна це переписати більш чітко? Я розумію те, що ви говорите, але, як каже Сурма, схоже, ви говорите щось інше. (і те, що, схоже, ви кажете, є неправильним)
Ліндон Вайт

3

Розуміння генератора - це простий спосіб створення генераторів з певною структурою. Скажімо, ви хочете, generatorщоб виводило одне за одним усі парні числа в your_list. Якщо ви створите його за допомогою стилю функції, це буде так:

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

evens = allEvens( yourList )

Ви могли б досягти того ж результату за допомогою цього виразу розуміння генератора:

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

В обох випадках, коли ви телефонуєте, next(evens)ви отримуєте наступний парний номер your_list.


0

Розуміння генератора - це підхід до створення ітерабелів, щось на зразок курсору, який рухається по ресурсу. Якщо ви знаєте курсор mysql або курсор mongodb, ви можете знати, що всі фактичні дані ніколи не завантажуються в пам'ять відразу, а по черзі. Курсор рухається вперед-назад, але в пам'яті завжди є один елемент рядка / списку.

Коротше кажучи, використовуючи розуміння генераторів, ви можете легко створити курсори в python.


-1

Ще один приклад розуміння генератора:

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 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.