如何让生成器/迭代器在用尽时评估为 False?
问题描述
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.
相关文章