有没有一种简单的方法来测试生成器是否没有任何项,例如peek
、hasNext
、isEmpty
等类似方法?
我知道这篇文章已经有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)
gen
生成器一次,因此副作用不是太严重的问题。但它将存储通过b
而非通过a
从生成器中提取的所有内容的副本,因此内存影响类似于只运行list(gen)
并进行检查。 - Matthias Frippdef 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()
>>> gen = (i for i in [])
>>> next(gen)
Traceback (most recent call last):
File "<pyshell#43>", line 1, in <module>
next(gen)
StopIteration
在生成器的结尾处会引发StopIteration
异常,因为在你的情况下,立即到达了结尾,所以异常被引发。但通常情况下不应检查是否存在下一个值。
另一件事情是:
>>> gen = (i for i in [])
>>> if not list(gen):
print('empty generator')
from cytoolz import peek
from typing import Tuple, Iterable
def is_empty_iterator(g: Iterable) -> Tuple[Iterable, bool]:
try:
_, g = peek(g)
return g, False
except StopIteration:
return g, True
只需使用 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
eog = object()
代替假设可迭代对象中永远不会出现float('-inf')
。 - bfontaine为了提供我的"2分钱"帮助,我将描述我的经验:
我有一个生成器,我需要使用itertools.islice
对其进行切片,以生成小的生成器。然后,为了检查我的子生成器是否为空,我只需将它们转换/消耗成一个小列表,并检查该列表是否为空。
例如:
from itertools import islice
def generator(max_yield=10):
a = 0
while True:
a += 1
if a > max_yield:
raise StopIteration()
yield a
tg = generator()
label = 1
while True:
itg = list(islice(tg, 3))
if not itg: # <-- I check if the list is empty or not
break
for i in itg:
print(f'#{label} - {i}')
label += 1
输出:
#1 - 1
#1 - 2
#1 - 3
#2 - 4
#2 - 5
#2 - 6
#3 - 7
#3 - 8
#3 - 9
#4 - 10
如果你需要在使用生成器之前知道,那么没有简单的方法。如果你可以等到使用生成器之后再知道,那么有一个简单的方法:
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()
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"
我的问题特殊之处在于可迭代对象要么为空,要么具有完全相同数量的条目。
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')
{% if content_generator %}
<section>
<h4>Section title</h4>
{% for item in content_generator %}
{{ item }}
{% endfor %
</section>
{% endif %}
[]
是方便的 Falsey 值,所以你可以对其进行 if 检查,并为某些特殊情况或无内容设置特殊行为。即使生成器没有生成任何元素,它们也是真实的。 - jpsimonsglob.iglob("filepattern")
来处理用户提供的通配符模式,并且如果该模式没有匹配到任何文件,我想要警告用户。当然,我可以通过各种方式解决这个问题,但是能够干净地测试迭代器是否为空是很有用的。 - LarsH