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


当前回答

我发现只有这个解决方案也适用于空迭代。

def is_generator_empty(generator):
    a, b = itertools.tee(generator)
    try:
        next(a)
    except StopIteration:
        return True, b
    return False, b

is_empty, generator = is_generator_empty(generator)

或者如果你不想使用异常来尝试使用

def is_generator_empty(generator):
    a, b = itertools.tee(generator)
    for item in a:
        return False, b
    return True, b

is_empty, generator = is_generator_empty(generator)

在标记的解决方案中,您不能将其用于空生成器,如

def get_empty_generator():
    while False:
        yield None 

generator = get_empty_generator()

其他回答

在我的例子中,我需要知道在我将一组生成器传递给一个函数之前,它是否已经填充,该函数合并了这些项,即zip(…)。解决方案与公认的答案相似,但又有足够的不同:

定义:

def has_items(iterable):
    try:
        return True, itertools.chain([next(iterable)], iterable)
    except StopIteration:
        return False, []

用法:

def filter_empty(iterables):
    for iterable in iterables:
        itr_has_items, iterable = has_items(iterable)
        if itr_has_items:
            yield iterable


def merge_iterables(iterables):
    populated_iterables = filter_empty(iterables)
    for items in zip(*populated_iterables):
        # Use items for each "slice"

我的特定问题具有这样的属性,即可迭代对象要么为空,要么具有完全相同数量的条目。

我意识到这篇文章已经5年了,但我在寻找一种惯用的方法时发现了它,并没有看到我的解决方案发布出来。所以为了子孙后代:

import itertools

def get_generator():
    """
    Returns (bool, generator) where bool is true iff the generator is not empty.
    """
    gen = (i for i in [0, 1, 2, 3, 4])
    a, b = itertools.tee(gen)
    try:
        a.next()
    except StopIteration:
        return (False, b)
    return (True, b)

当然,我相信许多评论员会指出,这很俗气,而且只在某些有限的情况下有效(例如,生成器没有副作用)。YMMV。

简单地用itertools包装生成器。Chain,在第二个可迭代对象中放入一个表示可迭代对象的结尾的东西,然后简单地检查它。

Ex:

import itertools

g = some_iterable
eog = object()
wrap_g = itertools.chain(g, [eog])

现在剩下的就是检查我们附加到可迭代对象末尾的值,当你读取它时,它将表示结束

for value in wrap_g:
    if value == eog: # DING DING! We just found the last element of the iterable
        pass # Do something

刚刚读到这篇文章,意识到缺少一个非常简单易懂的答案:

def is_empty(generator):
    for item in generator:
        return False
    return True

如果我们不打算使用任何项,那么我们需要将第一项重新注入生成器:

def is_empty_no_side_effects(generator):
    try:
        item = next(generator)
        def my_generator():
            yield item
            yield from generator
        return my_generator(), False
    except StopIteration:
        return (_ for _ in []), True

例子:

>>> g=(i for i in [])
>>> g,empty=is_empty_no_side_effects(g)
>>> empty
True
>>> g=(i for i in range(10))
>>> g,empty=is_empty_no_side_effects(g)
>>> empty
False
>>> list(g)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

一种简单的方法是使用next()的可选参数,如果生成器耗尽(或为空),则使用该参数。例如:

_exhausted  = object()

if next(some_generator, _exhausted) is _exhausted:
    print('generator is empty')