一行程序来检查迭代器是否至少产生一个元素?

目前我正在做的是:

try:
something = iterator.next()
# ...
except StopIteration:
# ...

但是我想要一个表达式,我可以把它放在一个简单的 if语句中。 有没有什么内置的东西可以让这段代码看起来不那么笨拙?

如果迭代器为空,则 any()返回 False,但如果不为空,则可能会迭代所有项。 我只需要它来检查第一项。


有人问我想做什么。 我已经编写了一个函数,它执行一个 SQL 查询并生成其结果。 有时候,当我调用这个函数时,我只想知道查询是否返回了任何东西,然后根据返回的东西做出决定。

70918 次浏览

__length_hint__ 估计 list(it)的长度-这是私有方法,不过:

x = iter( (1, 2, 3) )
help(x.__length_hint__)
1 Help on built-in function __length_hint__:
2
3 __length_hint__(...)
4     Private method returning an estimate of len(list(it)).

这并不是真正干净,但它展示了一种无损地将其包装在函数中的方法:

def has_elements(iter):
from itertools import tee
iter, any_check = tee(iter)
try:
any_check.next()
return True, iter
except StopIteration:
return False, iter


has_el, iter = has_elements(iter)
if has_el:
# not empty

这并不是真正的 pythonic,对于特定的情况,可能有更好的(但不太一般的)解决方案,如 下一个缺省值。

first = next(iter, None)
if first:
# Do something

这并不一般,因为在许多可迭代文件中,Nothing 可以是有效的元素。

你可使用:

if zip([None], iterator):
# ...
else:
# ...

但对于代码阅读器来说有点不明白

这是一个过度杀伤迭代器包装器,通常允许检查是否有下一个项(通过转换为布尔值)。当然效率很低。

class LookaheadIterator ():


def __init__(self, iterator):
self.__iterator = iterator
try:
self.__next      = next (iterator)
self.__have_next = True
except StopIteration:
self.__have_next = False


def __iter__(self):
return self


def next (self):
if self.__have_next:
result = self.__next
try:
self.__next      = next (self.__iterator)
self.__have_next = True
except StopIteration:
self.__have_next = False


return result


else:
raise StopIteration


def __nonzero__(self):
return self.__have_next


x = LookaheadIterator (iter ([]))
print bool (x)
print list (x)


x = LookaheadIterator (iter ([1, 2, 3]))
print bool (x)
print list (x)

产出:

False
[]
True
[1, 2, 3]

在 Python 2.6 + 中,如果名称 sentinel绑定到迭代器不可能产生的值,

if next(iterator, sentinel) is sentinel:
print('iterator was empty')

如果您不知道迭代器可能产生什么结果,那么使用

sentinel = object()

否则,您可以在哨兵角色中使用迭代器不可能产生的任何您“知道”的值(基于应用程序的考虑)。

如果 any为 True,则它不会超出第一个元素。如果迭代器产生一些错误的结果,您可以编写 any(True for _ in iterator)

有点晚了,但是... ... 您可以将迭代器转换成一个列表,然后使用该列表:

# Create a list of objects but runs out the iterator.
l = [_ for _ in iterator]


# If the list is not empty then the iterator had elements; else it was empty.
if l :
pass # Use the elements of the list (i.e. from the iterator)
else :
pass # Iterator was empty, thus list is empty.

那么:

In [1]: i=iter([])


In [2]: bool(next(i,False))
Out[2]: False


In [3]: i=iter([1])


In [4]: bool(next(i,False))
Out[4]: True

最好的方法是使用 more_itertools中的 peekable

from more_itertools import peekable
iterator = peekable(iterator)
if iterator:
# Iterator is non-empty.
else:
# Iterator is empty.

只是要注意,如果你一直引用旧的迭代器,那个迭代器将得到高级。从那时起,您必须使用新的可窥视迭代器。peekable希望成为修改旧迭代器的唯一代码位。