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

时间:2010-06-24 22:06:34

标签: python iterator

目前我正在这样做:

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

但我想要一个表达式,我可以放在一个简单的if语句中。 是否有任何内置功能可以使这些代码看起来不那么笨拙?

如果iterable为空,则

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


有人问我要做什么。 我编写了一个执行SQL查询并生成结果的函数。 有时当我调用此函数时,我只想知道查询是否返回任何内容并根据它做出决定。

9 个答案:

答案 0 :(得分:119)

如果它是真的,那么

any将不会超越第一个元素。如果迭代器产生错误,你可以写any(True for _ in iterator)

答案 1 :(得分:32)

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

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

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

创建自己的标记(例如,在模块的顶部)
sentinel = object()

否则,您可以在sentinel角色中使用您“知道”(基于应用程序注意事项)迭代器无法产生的任何值。

答案 2 :(得分:19)

这不是很干净,但它显示了一种无损地将其打包在函数中的方法:

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,对于特定情况,可能有更好(但不太通用)的解决方案,例如next默认。

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

这不是通用的,因为None可以是许多迭代中的有效元素。

答案 3 :(得分:5)

你可以使用:

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

但是对于代码阅读器来说这有点无法解释

答案 4 :(得分:2)

怎么样:

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

答案 5 :(得分:2)

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

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

请注意,如果您保留对旧迭代器的引用,该迭代器将变得高级。从那时起,您必须使用新的可窥视迭代器。不过,实际上,peekable期望是修改旧迭代器的唯一代码,因此无论如何您都不应该保留对旧迭代器的引用。

答案 6 :(得分:-1)

__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)).

答案 7 :(得分:-1)

这是一个过度的迭代器包装器,它通常允许检查是否有下一个项目(通过转换为boolean)。当然效率很低。

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]

答案 8 :(得分:-1)

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

# 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.