有没有一种简单的方法来测试生成器是否没有任何项,例如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.
return first, itertools.chain([first], rest)
这行代码中,我不太明白为什么要将第一个元素返回两次。 - njzk2def gen(): for pony in range(4): yield None if pony == 2 else pony
- PaulNone
,而是引发StopIteration
——函数的结果为None
。否则,它是一个元组,不是None
。 - anonpeek
调用会不会创建一个无休止的itertools.chain
对象链,其中包含对其他itertools.chain
对象的引用? - Mateen Ulhaq对于你的问题,简单回答:没有简单的方法。有很多变通方法。
实际上,不应该有简单的方法,因为生成器的作用是输出一个值序列而无需在内存中保存整个序列。所以无法进行反向遍历。
如果需要,您可以编写has_next函数,或者甚至通过使用装饰器将其附加到生成器作为方法。
一个简单的方法是使用 next() 的可选参数,如果生成器已经用尽(或为空),则使用该参数。例如:
_exhausted = object()
if next(some_generator, _exhausted) is _exhausted:
print('generator is empty')
next(iter([-1, -2, -3]), -1) == -1
是正确的。换句话说,任何第一个元素等于-1
的可迭代对象都将在使用您的条件时显示为空。 - Jeyekomonobject()
是一个非常特殊的值,它不会被包含在生成器中。 - Mikko Koho快速脏解决方案:
next(my_generator(), None) is not None
或者将 None
替换为您知道在生成器中不存在的任何值。
编辑:是的,这将跳过生成器中的1个项目。但有时,我仅出于验证目的检查生成器是否为空,然后不会真正使用它。否则,我会做类似以下的事情:
def foo(self):
if next(self.my_generator(), None) is None:
raise Exception("Not initiated")
for x in self.my_generator():
...
my_generator()
时,这个方法才会生效。None
。 - Mikko Koho在我看来,最好的方法是避免特殊的测试。大多数情况下,使用生成器本身就是一种测试:
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
将包含生成的最后一个值。如果没有生成任何内容,则它将是未定义的 - 除非您已经定义了该变量。您可以检查thing
的值,但这有点不可靠。相反,在块内设置一个标志,然后在之后检查它:
if not thing_generated:
print "Avast, ye scurvy dog!"
range(10000000)
是一个有限生成器(Python 3),但你不需要遍历所有项才能找出它是否会生成某些东西。 - Viktor Stískalaclass Pushable:
def __init__(self, iter):
self.source = iter
self.stored = []
def __iter__(self):
return self
def __bool__(self):
if self.stored:
return True
try:
self.stored.append(next(self.source))
except StopIteration:
return False
return True
def push(self, value):
self.stored.append(value)
def peek(self):
if self.stored:
return self.stored[-1]
value = next(self.source)
self.stored.append(value)
return value
def __next__(self):
if self.stored:
return self.stored.pop()
return next(self.source)
我刚看到这个帖子,发现一个非常简单易懂的答案还未出现:
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]
我不太愿意提供第二种解决方案,特别是一种我自己不会使用的方案,但是如果你绝对必须这样做,并且不想像其他答案那样消耗生成器:
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)
我真的不喜欢这个解决方案,因为我认为这不是生成器的正确使用方式。
要检查一个生成器是否为空,您只需要尝试获取下一个结果。当然,如果您还没有准备好使用该结果,则必须将其存储以便稍后返回。
以下是一个包装器类,可以添加到现有迭代器中以添加一个__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'
抱歉这种方法显而易见,但是最好的方法是这样做:
for item in my_generator:
print item
现在您检测到在使用生成器时它为空。当然,如果生成器是空的话,就不会显示任何项目。
这可能不完全符合您的代码,但这就是生成器习语的作用:通过迭代来实现,因此也许您需要稍微改变一下自己的方法,或者干脆不使用生成器。
[]
是方便的 Falsey 值,所以你可以对其进行 if 检查,并为某些特殊情况或无内容设置特殊行为。即使生成器没有生成任何元素,它们也是真实的。 - jpsimonsglob.iglob("filepattern")
来处理用户提供的通配符模式,并且如果该模式没有匹配到任何文件,我想要警告用户。当然,我可以通过各种方式解决这个问题,但是能够干净地测试迭代器是否为空是很有用的。 - LarsH