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

2022-01-10 00:00:00 python generator boolean iterator

问题描述

Python 中的其他空对象评估为 False —— 我怎样才能让迭代器/生成器也这样做?

Other empty objects in Python evaluate as False -- how can I get iterators/generators to do so as well?


解决方案

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

By default all objects in Python evaluate as True. In order to support False evaluations the object's class must have either a __len__ method (0 -> False), or a __nonzero__ method (False -> False). Note: __nonzero__ ==> __bool__ in Python 3.x.

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

Because the iterator protocol is intentionally kept simple, and because there are many types of iterators/generators that aren't able to know if there are more values to produce before attempting to produce them, True/False evaluation is not part of the iterator protocol.

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

If you really want this behavior, you have to provide it yourself. One way is to wrap the generator/iterator in a class that provides the missing functionality.

请注意,此代码仅在引发 StopIteration 后评估为 False .

Note that this code only evaluates to False after StopIteration has been raised.

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

As a bonus, this code works for pythons 2.4+

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 before StopIteration被提出):

If you also want look-ahead (or peek) behavior, this code will do the trick (it evaluates to False before StopIteration is raised):

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

请记住,当底层迭代器/生成器的时间与其产生的值相关时,窥视行为是不合适的.

Keep in mind that peek behaviour is not appropriate when the timing of the underlying iterator/generator is relevant to its produced values.

另外请记住,第三方代码,可能还有标准库,可能依赖于迭代器/生成器,总是评估为 True.如果你想在没有 bool 的情况下查看,请删除 __nonzero____bool__ 方法.

Also keep in mind that third-party code, and possibly the stdlib, may rely on iterators/generators always evaluating to True. If you want peek without bool, remove the __nonzero__ and __bool__ methods.

相关文章