是否有一种简单的方法来测试生成器是否没有项目,比如peek, hasNext, isEmpty之类的?


当前回答

建议:

def peek(iterable):
    try:
        first = next(iterable)
    except StopIteration:
        return None
    return first, itertools.chain([first], iterable)

用法:

res = peek(mysequence)
if res is None:
    # sequence is empty.  Do stuff.
else:
    first, mysequence = res
    # Do something with first, maybe?
    # Then iterate over the sequence:
    for element in mysequence:
        # etc.

其他回答

要查看生成器是否为空,只需尝试获得下一个结果。当然,如果你还没有准备好使用这个结果,那么你必须存储它,以便以后再次返回。

下面是一个包装器类,可以添加到现有的迭代器中以添加__nonzero__测试,因此您可以通过简单的if查看生成器是否为空。它也可以变成一个装饰器。

class GenWrapper:
    def __init__(self, iter):
        self.source = iter
        self.stored = False

    def __iter__(self):
        return self

    def __nonzero__(self):
        if self.stored:
            return True
        try:
            self.value = next(self.source)
            self.stored = True
        except StopIteration:
            return False
        return True

    def __next__(self):  # use "next" (without underscores) for Python 2.x
        if self.stored:
            self.stored = False
            return self.value
        return next(self.source)

下面是你如何使用它:

with open(filename, 'r') as f:
    f = GenWrapper(f)
    if f:
        print 'Not empty'
    else:
        print 'Empty'

请注意,您可以在任何时候检查空,而不仅仅是在迭代的开始。

恕我直言,最好的办法是避免特殊测试。大多数时候,使用生成器是一种测试:

thing_generated = False

# Nothing is lost here. if nothing is generated, 
# the for block is not executed. Often, that's the only check
# you need to do. This can be done in the course of doing
# the work you wanted to do anyway on the generated output.
for thing in my_generator():
    thing_generated = True
    do_work(thing)

如果这还不够好,您仍然可以执行显式测试。此时,thing将包含最后生成的值。如果没有生成任何内容,它将是未定义的—除非您已经定义了该变量。你可以检查东西的价值,但那有点不可靠。相反,只需在块内设置一个标志,然后检查它:

if not thing_generated:
    print "Avast, ye scurvy dog!"

我讨厌提供第二个解决方案,特别是一个我自己不会使用的解决方案,但是,如果你绝对必须这样做,并且不消耗生成器,就像在其他答案中一样:

def do_something_with_item(item):
    print item

empty_marker = object()

try:
     first_item = my_generator.next()     
except StopIteration:
     print 'The generator was empty'
     first_item = empty_marker

if first_item is not empty_marker:
    do_something_with_item(first_item)
    for item in my_generator:
        do_something_with_item(item)

现在我真的不喜欢这个解决方案,因为我相信这不是生成器的使用方式。

在遍历生成器之前检查生成器符合LBYL编码风格。另一种方法(EAFP)是遍历它,然后检查它是否为空。

is_empty = True

for item in generator:
    is_empty = False
    do_something(item)

if is_empty:
    print('Generator is empty')

这种方法也可以很好地处理无限生成器。

对你的问题的简单回答是:不,没有简单的方法。有很多变通办法。

不应该有一种简单的方法,因为生成器是一种输出值序列而不将序列保存在内存中的方法。所以没有向后遍历。

你可以写一个has_next函数,或者甚至可以把它作为一个带有花哨装饰器的方法放在生成器上。