如何在Python生成器中向前查看一个元素(peek)?

107 投票
19 回答
60022 浏览
提问于 2025-04-15 20:18

我搞不清楚怎么在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())

有没有人能帮我写一个可以向前看一个元素的生成器?


另见: 重置Python中的生成器对象

19 个回答

24

好的,虽然我来得有点晚,但我看到这个问题,发现没有一个答案让我满意。于是我想出了这个元生成器:

class Peekorator(object):

    def __init__(self, generator):
        self.empty = False
        self.peek = None
        self.generator = generator
        try:
            self.peek = self.generator.next()
        except StopIteration:
            self.empty = True

    def __iter__(self):
        return self

    def next(self):
        """
        Return the self.peek element, or raise StopIteration
        if empty
        """
        if self.empty:
            raise StopIteration()
        to_return = self.peek
        try:
            self.peek = self.generator.next()
        except StopIteration:
            self.peek = None
            self.empty = True
        return to_return

def simple_iterator():
    for x in range(10):
        yield x*3

pkr = Peekorator(simple_iterator())
for i in pkr:
    print i, pkr.peek, pkr.empty

结果是:

0 3 False
3 6 False
6 9 False
9 12 False    
...
24 27 False
27 None False

也就是说,在你遍历列表的过程中,随时都可以访问到下一个项目。

105

为了完整性,more-itertools这个包(应该是每个Python程序员工具箱里必备的)里有一个叫peekable的功能,可以实现这种效果。就像文档中的代码示例所展示的那样:

>>> p = peekable(['a', 'b'])
>>> p.peek()
'a'
>>> next(p)
'a'

不过,很多时候我们可以重写那些需要这个功能的代码,让它其实不需要这个功能。比如,你在问题中提到的代码示例可以这样写:

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

(读者注意:我保留了问题中示例的语法,尽管它提到的是一个过时的Python版本)

80

Python的生成器是一种特殊的工具,它的工作方式是你不能把已经读取过的元素再放回去。不过,你可以使用itertools模块来创建一个新的迭代器,并把元素放到最前面。

import itertools

gen = iter([1,2,3])
peek = gen.next()
print list(itertools.chain([peek], gen))

撰写回答