2 апреля 2013 г.

Генераторы - объекты итераторов однократного применения


Функции-генераторы, выражения-генераторы имеют собственные
итераторы и поддерживают возможность лишь однократного выполнения итераций.


Например, для выражения-генератора из предыдущего поста итератором является сам генератор (фактически вызов
метода iter генератора не выполняет никаких действий):
>>> G = (c * 4 for c in ‘SPAM’)
>>> iter(G) is G # Итератором генератора является сам генератор: 
True # G имеет метод __next__



Если выполнить обход результатов вручную, с помощью нескольких итераторов, окажется, что все они ссылаются на одну и ту же позицию в последовательности результатов:
>>> G = (c * 4 for c in ‘SPAM’) # Создать новый генератор
>>> I1 = iter(G) # Выполнить итерацию вручную
>>> next(I1)
‘SSSS’
>>> next(I1)
‘PPPP’
>>> I2 = iter(G) # Второй итератор 
>>> next(I2) # ссылается на ту же позицию!
‘AAAA’


Кроме того, как только итерации достигнут конца, все результаты окажутся исчерпаны – чтобы выполнить повторный обход результатов, нам придется создать новый генератор:
>>> list(I1) # Выберет остатки результаов в I1
[‘MMMM’]
>>> next(I2) # Другие итераторы также окажутся исчерпанными
StopIteration
>>> I3 = iter(G) # То же относится и к вновь созданным итераторам
>>> next(I3)

StopIteration
>>> I3 = iter(c * 4 for c in ‘SPAM’) # Создать новый генератор, чтобы 
>>> next(I3) # выполнить повторный обход результатов
‘SSSS’


То же относится и к функциям-генераторам– следующий эквивалентный генератор, реализованный в виде функции, также обеспечивает возможность однократного выполнения итераций:
>>> def timesfour(S):
... for c in S:
... yield c * 4
...
>>> G = timesfour(‘spam’) # Функция-генератор действует точно так же
>>> iter(G) is G
True
>>> I1, I2 = iter(G), iter(G)
>>> next(I1)
‘ssss’
>>> next(I1)
‘pppp’
>>> next(I2) # I2 находится в той же позиции, что и I1
‘aaaa’


Некоторые встроенные типы имеют отличное поведение, поддерживают возможность создания множества независимых итераторов и способны отражать изменения объекта во всех активных итераторах:
>>> L = [1, 2, 3, 4]
>>> I1, I2 = iter(L), iter(L)
>>> next(I1)
1
>>> next(I1)
2
>>> next(I2) # Списки поддерживают множество независимых итераторов
1
>>> del L[2:] # Изменения отражаются на всех итераторах
>>> next(I1)
StopIteration







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

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