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

Eth*_*man 16 python iterator boolean generator

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

Ray*_*ger 8

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

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

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

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

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


Eth*_*man 6

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

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

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

请注意,此代码仅在引发False 之后 进行求值StopIteration.

作为奖励,此代码适用于蟒蛇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
Run Code Online (Sandbox Code Playgroud)

如果你还想要前瞻(或偷看)行为,这个代码将完成这个诀窍(它的评估结果是False 之前 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
Run Code Online (Sandbox Code Playgroud)

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

还要记住,第三方代码以及可能的stdlib可能依赖于始终进行求值的迭代器/生成器True.如果你想偷看没有bool,删除__nonzero____bool__方法.