Можно ли использовать генератор более одного раза?

Это мой фрагмент кода с двумя определенными генераторами:

one_line_gen = (x for x in range(3))

def three_line_gen():
    yield 0
    yield 1
    yield 2

Когда я выполняю:

for x in one_line_gen:
    print x

for x in one_line_gen:
    print x

Результат ожидаемый:

0
1
2

Однако, если я выполню:

for x in three_line_gen():
    print x

for x in three_line_gen():
    print x

Результат:

0
1
2
0
1
2

Почему? Я думал, что любой генератор можно использовать только один раз.


person Hash Doe    schedule 30.07.2017    source источник
comment
Не путайте генераторную функцию с генератором, который она производит.   -  person Martijn Pieters    schedule 30.07.2017
comment
Иными словами, если бы вы использовали def one_line_gen(): return (x for x in range(3)), ваш первый пример был бы эквивалентен (создание генератора каждый раз, когда вы вызываете функцию).   -  person Martijn Pieters    schedule 30.07.2017
comment
Тесно связано: Выражение генератора против yield: почему не работает 'next()'?   -  person Martijn Pieters    schedule 30.07.2017


Ответы (5)


three_line_gen это не генератор, это функция. То, что он возвращает, когда вы вызываете его, является генератором, совершенно новым каждый раз, когда вы его вызываете. Каждый раз, когда вы ставите скобки следующим образом:

three_line_gen()

Это совершенно новый генератор, над которым нужно работать. Однако если бы вы сначала сделали

mygen = three_line_gen()

и повторите mygen дважды, во второй раз произойдет сбой, как вы ожидаете.

person Ofer Sadan    schedule 30.07.2017

нет, вы не можете перебирать генератор дважды. генератор исчерпан, как только вы перебрали его. вы можете сделать копию генератора с tee:

from itertools import tee

one_line_gen = (x for x in range(3))
gen1, gen2 = tee(one_line_gen)
# or: 
# gen1, gen2 = tee(x for x in range(3))

for item in gen1:
    print(item)

for item in gen2:
    print(item)

для других вопросов см. Ответ Садана.

person hiro protagonist    schedule 30.07.2017

Да, генератор можно использовать только один раз. но у вас есть два объекта-генератора.

# Python 3


def three_line_gen():
    yield 0
    yield 1
    yield 2

iterator = three_line_gen()
print(iterator)
for x in iterator:
    print(id(iterator), x)

iterator2 = three_line_gen()
print(iterator2)
for x in iterator2:
    print(id(iterator2), x)

И результат:

<generator object three_line_gen at 0x1020401b0>
4328784304 0
4328784304 1
4328784304 2
<generator object three_line_gen at 0x1020401f8>
4328784376 0
4328784376 1
4328784376 2
person Mohsenasm    schedule 30.07.2017

Почему? Я думал, что любой генератор можно использовать только один раз.

Потому что каждый вызов three_line_gen() создает новый генератор.

В противном случае вы правы в том, что генераторы работают только вперед, пока не иссякнут.

Можно ли использовать генератор более одного раза?

Да, возможно, если результаты буферизуются вне генератора. Один из простых способов — использовать itertools.tee():

>>> from itertools import tee
>>> def three_line_gen():
        yield 0
        yield 1
        yield 2

>>> t1, t2 = tee(three_line_gen())
>>> next(t1)
0
>>> next(t2)
0
>>> list(t1)
[1, 2]
>>> list(t2)
[1, 2]
person Raymond Hettinger    schedule 26.08.2017

Потому что в одном вкладыше находится Generator Object, а в трех вкладышах — function. Они хотели быть другими.

Эти двое похожи.

def three_line_gen_fun():
    yield 0
    yield 1
    yield 2

three_line_gen = three_line_gen_fun()
one_line_gen = (x for x in range(3))

type(three_line_gen) == type(one_line_gen)
person Rahul    schedule 30.07.2017