Python 如何使生成器/迭代器在耗尽时计算为False?
Python中的其他空对象的计算结果为False——如何让迭代器/生成器也这样做?默认情况下,Python中的所有对象的计算结果为Python 如何使生成器/迭代器在耗尽时计算为False?,python,iterator,boolean,generator,Python,Iterator,Boolean,Generator,Python中的其他空对象的计算结果为False——如何让迭代器/生成器也这样做?默认情况下,Python中的所有对象的计算结果为True。为了支持False求值,对象的类必须具有方法(0->False),或非零方法(False)。注意:Python3.x中的\uuuuu非零==>\uuuuuu bool\uuuu 由于迭代器协议有意保持简单,并且由于有许多类型的迭代器/生成器无法在尝试生成它们之前知道是否有更多的值要生成,True/False计算不是迭代器协议的一部分 如果你真的想要这种行为,
True
。为了支持False
求值,对象的类必须具有方法(0
->False
),或非零方法(False
)。注意:Python3.x中的\uuuuu非零
==>\uuuuuu bool\uuuu
由于迭代器协议有意保持简单,并且由于有许多类型的迭代器/生成器无法在尝试生成它们之前知道是否有更多的值要生成,True
/False
计算不是迭代器协议的一部分
如果你真的想要这种行为,你必须自己提供。一种方法是将生成器/迭代器包装在一个提供缺少的功能的类中
请注意,此代码仅在引发StopIteration
后计算为False
作为奖励,此代码适用于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
如果您还需要前瞻(或窥视)行为,则此代码将执行此操作(在引发StopIteration
之前,其计算结果为False
):
请记住,当底层迭代器/生成器的计时与其生成的值相关时,peek行为是不合适的
还要记住,第三方代码,可能还有stdlib,可能依赖迭代器/生成器,其计算结果总是True
。如果希望peek不使用bool,请删除\uuuu非零\uuuu
和\uuuuu bool\uuuu
方法。Guido不希望生成器和迭代器以这种方式运行
默认情况下,对象为true。只有当它们定义了返回零的_len _uu或返回false的_non zero u(后者在Py3.x中称为_bool u)时,它们才可能为false
您可以将其中一个方法添加到自定义迭代器中,但它与Guido的意图不匹配。他拒绝在已知下一个长度的迭代器中添加_len__。这就是为什么我们得到了长度提示
因此,判断迭代器是否为空的唯一方法是对其调用next()并查看它是否引发StopIteration
在ASPN上,我相信有一些使用这种技术的前瞻包装器的方法。如果提取了一个值,它将保存到下一个()调用中。一个“空的东西”自动不是迭代器。容器可以是空的,也可以不是空的,您可以在容器上获得迭代器,但是这些迭代器在耗尽时不会出错
迭代器不会出错的一个很好的例子是sys.stdin
。当sys.stdin
到达输入端时,将其设为false的问题在于,如果不尝试使用它的输入,就无法真正知道您是否已经到达了这样一个流的末尾。希望迭代器为假的主要原因是“偷看”下一项是否有效;但是对于sys.stdin
,这显然是不实际的
这是另一个例子
(x for x in xrange(1000) if random.randrange(0, 2))
如果不做大量的工作,就无法知道这个生成器是否会返回更多的数字,实际上,您必须找出下一个值是什么
解决方案是只从迭代器中获取下一个值。如果它是空的,则循环将退出,如果不在循环中,则会出现StopIteration
异常 我并不是建议所有迭代器/生成器都应该这样做,只是有时候这样做对它们很有用。对于那些时候,我的回答提供了一种实现方法。Guido建议,迭代器/生成器永远不应该以这种方式运行。Python的一个优点是,如果您需要/想要做与“approved”方法不同的事情,它(通常)不会妨碍您。(不要让我开始讲sum()
ingstr
s!;)这就是所谓的“违背语言的本质”。这意味着您的迭代器在假定bool(It)
始终为真的代码中不可用。Guido能够生成此类代码的示例,包括他编写的一些代码。(仅仅因为您可以指定False,True=1,0并不意味着您应该;-)确实如此!你能发一个链接到这样的代码吗?我很好奇为什么人们会费心检查总是True
的东西的真值。空迭代器仍然是迭代器,虽然偷看是主要原因,但不是唯一原因。此外,在布尔测试中使用StopIteration
也很笨拙;只有容器可以是空的。迭代器是一个“地方”,比如“在开头”,或者“第23行的第5列”。我必须考虑一下。尽管如此,即使按照这些思路,我的代码也可以被认为是True
如果不是“结束”,那么False
否则。当您从sys.stdin
读取时,当按下ctrl-D
时,迭代器将引发StopException
好像它是空的,但实际上流不会关闭,它实际上“重新打开”,允许进一步阅读。在所有迭代器类型中都没有一致的空概念。唯一一致可用的功能是next()
,它可能会引发StopIteration
,也可能不会。你说的“StopException”——你是说StopIteration
?一旦提出了StopIteration
,就应该在进一步的next()
调用或迭代器有趣的问答中继续提出它,可以利用inspect.getgeneratorstate()
来实现这一点吗?
(x for x in xrange(1000) if random.randrange(0, 2))