вторник, 18 сентября 2018 г.

Генераторы и итераторы (экстракт из М.Лутца)


Марк Лутц неоднократно писал о важности понимания генераторов и итераторов. Здесь я коротко изложу основные положения из важной главы № 20 книги "Изучаем python".




Отображение операций на последовательности и сбор результатов - очень частая задача. Можно это делать через:

  1. цикл for; 
  2. функциональное программирование (например функция map); 
  3. разовую функцию lambda. 
  4. Но лучше всего через генератор списка. Пример:
res = [ord(x) for x in 'spam']
res
[115,112,97,109]

В отличие от функции map генератор отображает выражение на последовательность и сразу выдаёт весь результат. Он гибкий (можно сделать вложенные циклы, можно обрабатывать вложенные списки т.е. матрицы) и быстрее всех аналогов.


Но может понадобится получать не все результаты сразу, а по требованию. Это возможно реализовать двумя способами.

Функция генератор и инструкция yield (глагол переводится как 'уступать').

Обычная функция возвращает всю последовательность сразу, после чего завершает работу. 
Функция-генератор приостанавливает и возобновляет выполнение работы, воспроизводя последовательность значений. При вызове возвращается объект генератора. 

def createGenerator():
    mylist = range(3)
    for i in mylist:
        yield i*i                                          # yield вместо return, т.е. замораживание  состояния

mygenerator = createGenerator()                                   # создать генератор
print(mygenerator)                                  
<generator object createGenerator at 0xb7555c34>      # выдаёт объект
for i in mygenerator:                                                       # итерируем объект но только 1 раз!
    print(i)
0
1
4

В 3-й версии языка появилась новая конструкция - выражение-генератор. Внешне похоже на генератор списков, но в круглых скобках. Возвращает объект-генератор, который поддерживает протокол итераций, возвращая по одному элементу списка за раз. Но тоже - можно его проитерировать только однократно.

 g = (x ** 2 for i in range(4))
 next(g)
0
 next(g)
1
 next(g)
2
 next(g)
3
 next(g)
StopIteration


Также в 3й версии языка добавились генераторы множеств и словарей. Они как генераторы списков - возвращают всю последовательность сразу.
Генератор множеств:
    {x*x for x in range(5)}
{0,1,4,9,16}
Генератор словаря:
    {x:x*x for x in range(5)}
{0:0, 1:1, 2:4, 3:9, 4:16}


Комментариев нет:

Отправить комментарий