我怎么知道一个发电机从一开始是空的?

有没有简单的testing方法,如果发电机没有项目,如偷看,hasNext,isEmpty,沿着这些线?

简单的回答你的问题:不,没有简单的方法。 有很多的解决方法。

实际上不应该有一个简单的方法,因为生成器是什么:一种输出一系列值的方法, 而不需要在内存中保存序列 。 所以没有后向遍历。

你可以编写一个has_next函数,或者甚至可以把它作为一个花哨的装饰器的方法,如果你想要的话,把它拍到一个生成器上。

build议:

 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. 

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

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

编辑:纠正了mehtunguh的评论中指出的问题。

我讨厌提供第二个解决scheme,特别是我不会使用自己的解决scheme,但是,如果您绝对必须这样做,而不是像其他答案那样使用生成器:

 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) 

现在我真的不喜欢这个解决scheme,因为我相信这不是如何使用发电机。

恕我直言,最好的办法是避免一个特殊的testing。 大多数情况下,使用发生器testing:

 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) 

如果这还不够好,你仍然可以进行明确的testing。 在这一点上, thing将包含最后生成的值。 如果什么都没有生成,它将是未定义的 – 除非你已经定义了variables。 你可以检查一下thing的价值,但是这有点不可靠。 相反,只需在块内设置一个标志,然后检查:

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

next(generator, None) is not None

或者更换None但是不pipe你知道什么值都不在你的发电机中。

编辑 :是的,这将跳过生成器中的1项。 然而,通常我会检查一个生成器是否为空,仅用于validation目的,然后不真正使用它。 否则我会做类似的事情:

 def foo(self): if next(self.my_generator(), None) is None: raise Exception("Not initiated") for x in self.my_generator(): ... 

对不起,明显的做法,但最好的办法是做:

 for item in my_generator: print item 

现在您在使用时已经检测到发生器是空的。 当然,如果发生器是空的,项目将永远不会显示。

这可能不完全符合你的代码,但这是生成器的习惯用法:迭代,所以也许你可能会稍微改变你的方法,或者根本不使用生成器。

我意识到这个post现在已经5岁了,但是我在find这样做的地道方式的时候发现了这个post,并且没有看到我的解决scheme。 所以对于后人:

 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) 

当然,正如我相信很多评论家会指出的那样,这种做法很冒险,而且在某些有限的情况下(例如发电机无副作用),它只能起作用。 因人而异。

所有你需要做的,看看发电机是否是空的是尝试获得下一个结果。 当然,如果你还没有准备好使用这个结果,那么你必须把它存储起来,以后再返回。

这是一个包装类,可以添加到现有的迭代器添加一个__nonzero__testing,所以你可以看到,如果发电机是空的,用一个简单的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 = self.source.next() self.stored = True except StopIteration: return False return True def next(self): if self.stored: self.stored = False return self.value return self.source.next() 

以下是你如何使用它:

 with open(filename, 'r') as f: f = GenWrapper(f) if f: print 'Not empty' else: print 'Empty' 
 >>> gen = (i for i in []) >>> next(gen) Traceback (most recent call last): File "<pyshell#43>", line 1, in <module> next(gen) StopIteration 

在发生器结束时,会StopIteration ,因为在您的情况下立即达到结果,将引发exception。 但通常你不应该检查下一个值的存在。

你可以做的另一件事是:

 >>> gen = (i for i in []) >>> if not list(gen): print('empty generator') 

如果您使用发生器之前需要知道,那么不,没有简单的方法。 如果您可以等到使用发生器后,有一个简单的方法:

 was_empty = True for some_item in some_generator: was_empty = False do_something_with(some_item) if was_empty: handle_already_empty_generator_case() 

这里是我简单的方法,我用来继续返回一个迭代器,同时检查是否产生了我只是检查循环运行:

  n = 0 for key, value in iterator: n+=1 yield key, value if n == 0: print ("nothing found in iterator) break 

这是一个包装生成器的简单装饰器,所以如果为空则返回None。 如果您的代码需要知道生成器循环播放之前是否会生成任何内容这会非常有用。

 def generator_or_none(func): """Wrap a generator function, returning None if it's empty. """ def inner(*args, **kwargs): # peek at the first item; return None if it doesn't exist try: next(func(*args, **kwargs)) except StopIteration: return None # return original generator otherwise first item will be missing return func(*args, **kwargs) return inner 

用法:

 import random @generator_or_none def random_length_generator(): for i in range(random.randint(0, 10)): yield i gen = random_length_generator() if gen is None: print('Generator is empty') 

其中有用的一个例子是模板代码 – 例如jinja2

 {% if content_generator %} <section> <h4>Section title</h4> {% for item in content_generator %} {{ item }} {% endfor % </section> {% endif %} 

简单地用itertools.chain包装生成器,将代表可迭代结束的东西作为第二个迭代器,然后简单地检查一下。

例如:

 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 

使用islice你只需要检查到第一次迭代发现它是否是空的。

从itertools导入islice

def isempty(可迭代):
返回列表(islice(iterable,1))== []

在我的情况下,我需要知道是否有大量的发电机被填充之前,我把它传递给一个函数,合并的项目,即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" 

我特别的问题是iterables是空的或者具有完全相同数量的条目。

怎么使用任何()? 我用它与发电机,它工作正常。 这里有一个人解释一下这个

我使用sum函数解决了这个问题。 看下面的例子我用glob.iglob(它返回一个生成器)。

 def isEmpty(): files = glob.iglob(search) if sum(1 for _ in files): return True return False 

*这可能不适用于巨大的发电机,但应该很好地为较小的名单