如何在耗尽时让生成器/迭代器评估为False?

时间:2011-11-02 05:40:45

标签: python iterator boolean generator

Python中的其他空对象评估为False - 我如何才能获得迭代器/生成器呢?

3 个答案:

答案 0 :(得分:8)

Guido不希望生成器和迭代器以这种方式运行。

默认情况下,对象为true。只有当他们定义返回零的__len__或者返回 False 的__nonzero__(后者在Py3.x中称为__bool__)时,它们才可以是假的。

您可以将其中一种方法添加到自定义迭代器中,但它与Guido的意图不匹配。他拒绝将__len__添加到即将到来的长度已知的迭代器中。这就是我们得到__length_hint__的方式。

因此,判断迭代器是否为空的唯一方法是在其上调用 next()并查看它是否引发 StopIteration

在ASPN上,我相信有些配方使用这种技术进行前瞻性包装。如果获取了一个值,则会将其保存为即将进行的next()调用。

答案 1 :(得分:6)

默认情况下,Python中的所有对象都评估为True。为了支持False评估,对象的类必须采用__len__方法(0 - > False)或__nonzero__方法({{ 1}} - > False)。注意:False ==> Python 3.x中的__nonzero__

因为迭代器协议有意保持简单,并且因为有许多类型的迭代器/生成器在尝试生成它们之前无法知道是否有更多值要生成,__bool__ / {{ 1}}评估不是迭代器协议的一部分。

如果您真的想要这种行为,您必须自己提供。一种方法是将生成器/迭代器包装在提供缺少功能的类中。

请注意,此代码仅在 True被提升后评估为False

作为奖励,此代码适用于python 2.4 +

False

如果你还想要预见(或偷看)行为,这段代码就可以解决问题(在 StopIteration被引发之前评估为try: next except NameError: # doesn't show up until python 2.6 def next(iter): return iter.next() Empty = object() class Boolean_Iterator(object): """Adds the abilities True/False tests: True means there /may/ be items still remaining to be used """ def __init__(self, iterator): self._iter = iter(iterator) self._alive = True def __iter__(self): return self def __next__(self): try: result = next(self._iter) except StopIteration: self._alive = False raise return result next = __next__ # python 2.x def __bool__(self): return self._alive __nonzero__ = __bool__ # python 2.x

False

请记住,当底层迭代器/生成器的时间与其生成的值相关时,偷看行为是不合适的。

另请注意,第三方代码(可能还有stdlib)可能依赖于始终评估为StopIteration的迭代器/生成器。如果你想偷看没有布尔,请删除try: next except NameError: # doesn't show up until python 2.6 def next(iter): return iter.next() Empty = object() class Iterator(object): """Adds the abilities True/False tests: True means there are items still remaining to be used peek(): get the next item without removing it from the sequence """ def __init__(self, iterator): self._iter = iter(iterator) self._peek = Empty self.peek() def __next__(self): peek, self._peek = self._peek, Empty self.peek() if peek is not Empty: return peek raise StopIteration next = __next__ # python 2.x def __bool__(self): return self._peek is not Empty __nonzero__ = __bool__ # python 2.x def peek(self): if self._peek is not Empty: return self._peek self._peek = next(self._iter, Empty) return self._peek True方法。

答案 2 :(得分:4)

'empty thing'自动不是迭代器。容器可以是空的,也可以不是,你可以在容器上获得迭代器,但这些迭代器在耗尽时不会出现错误。

为什么迭代器不会变得虚假的一个很好的例子是sys.stdin。当sys.stdin到达输入结尾时出现问题的问题是,如果没有尝试从中输入输入,就无法确切知道是否已到达此类流的末尾。想要迭代器是假的主要原因是'偷看'看看下一个项目是否有效;但对于sys.stdin,这显然不切实际。

这是另一个例子

(x for x in xrange(1000) if random.randrange(0, 2))

没有办法知道这个生成器是否会在不做大量工作的情况下返回更多数字,你实际上必须找出下一个值是什么。

解决方案是从迭代器中获取下一个值。如果它为空,则您的循环将退出,或者如果您不在循环中,您将获得StopIteration异常。