Как посмотреть на один элемент (peek) в генераторе Python? - PullRequest
62 голосов
/ 11 марта 2010

Я не могу понять, как смотреть вперед на один элемент в генераторе Python. Как только я посмотрю, его уже нет.

Вот что я имею в виду:

gen = iter([1,2,3])
next_value = gen.next()  # okay, I looked forward and see that next_value = 1
# but now:
list(gen)  # is [2, 3]  -- the first value is gone!

Вот более реальный пример:

gen = element_generator()
if gen.next_value() == 'STOP':
  quit_application()
else:
  process(gen.next())

Может кто-нибудь помочь мне написать генератор, который вы можете посмотреть на один элемент вперед?

Ответы [ 14 ]

1 голос
/ 03 июня 2019

cytoolz имеет функцию peek .

>> from cytoolz import peek
>> gen = iter([1,2,3])
>> first, continuation = peek(gen)
>> first
1
>> list(continuation)
[1, 2, 3]
1 голос
/ 05 декабря 2017

w.r.t @ Сообщение Дэвида Z, более новый инструмент seekable может сбросить перенесенный итератор на предыдущую позицию.

>>> s = mit.seekable(range(3))
>>> s.next()
# 0

>>> s.seek(0)                                              # reset iterator
>>> s.next()
# 0

>>> s.next()
# 1

>>> s.seek(1)
>>> s.next()
# 1

>>> next(s)
# 2
1 голос
/ 19 июля 2017

Фрагмент Python3 для @ jonathan-hartley ответ:

def peek(iterator, eoi=None):
    iterator = iter(iterator)

    try:
        prev = next(iterator)
    except StopIteration:
        return iterator

    for elm in iterator:
        yield prev, elm
        prev = elm

    yield prev, eoi


for curr, nxt in peek(range(10)):
    print((curr, nxt))

# (0, 1)
# (1, 2)
# (2, 3)
# (3, 4)
# (4, 5)
# (5, 6)
# (6, 7)
# (7, 8)
# (8, 9)
# (9, None)

Было бы просто создать класс, который делает это на __iter__ и возвращает только элемент prev и помещает elm в некоторый атрибут.

1 голос
/ 05 февраля 2015

Хотя itertools.chain() является естественным инструментом для этой работы, остерегайтесь таких петель:

for elem in gen:
    ...
    peek = next(gen)
    gen = itertools.chain([peek], gen)

... Потому что это будет занимать линейно растущий объем памяти и в конечном итоге остановится. (Похоже, этот код создает связанный список, по одному узлу на вызов chain ().) Я знаю это не потому, что проверял библиотеки, а потому, что это привело к серьезному замедлению моей программы - избавлению от строки gen = itertools.chain([peek], gen) ускорил это снова. (Python 3.3)

...