我只是在Python解释器中四处乱逛,遇到了一些意外的行为。
>>> bools = (True, True, True, False)
>>> all(bools)
False
>>> any(bools)
True
好,到目前为止,没有什么异常...
>>> bools = (b for b in (True, True, True, False))
>>> all(bools)
False
>>> any(bools)
False
在这里事情开始变得怪异。我认为发生这种情况是因为all
函数遍历了生成器表达式,调用了它的__next__
方法并用尽了值,直到遇到False
为止。以下是支持该理论的一些证据:
>>> bools = (b for b in (True, False, True, True))
>>> all(bools)
False
>>> any(bools)
True
我认为结果有所不同,因为False
不在末尾,因此生成器中还剩下一些未使用的值。如果您输入
>>> bools = (b for b in (True, False, True, True))
>>> all(bools)
False
>>> list(bools)
[True, True]
似乎只有2个剩余值。
那么,为什么这确实发生了呢?我敢肯定我遗漏了许多细节。
答案 0 :(得分:8)
您遇到的问题是,在生成所有值之后您正在使用生成器。
您可以通过运行以下代码来验证这一点:
>>> bools = (b for b in (True, False, True, True))
>>> all(bools) # once the False is found it will stop producing values
True
>>> next(bools) # next value after False which is True
True
>>> next(bools) # next value after True which is True
True
>>> next(bools)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
这将起作用:
>>> bools = (b for b in (True, False, True, True))
>>> all(bools)
False
>>> bools = (b for b in (True, False, True, True))
>>> any(bools)
True
答案 1 :(得分:6)
通过伪代码:
def all(iterable):
for element in iterable:
if not element:
return False
return True
all()
仅使用True
个元素,它在找到第一个计算为False
的元素时终止。
def any(iterable):
for element in iterable:
if element:
return True
return False
any()
仅使用False
个元素,它在找到第一个计算为True
的元素时终止。
请注意,发电机在通过时不会复位到其初始位置。 除非消耗更多物品,否则它们将保持当前位置。因此,
>>> bools = (b for b in (True, False, True, True))
以下内容将消耗前两个项目。由于第二项是False
,因此迭代将在此之后停止。这样会将生成器留在第二个元素之后的位置。
>>> all(bools)
False
此时,生成器将(True, True)
作为剩余值。您在问题中正确指出了这一点。以下仅消耗一个元素。
>>> any(bools)
True
请注意,还可以获得另一个True
值
调用any()
后从生成器中获取。
当然,如果您在生成器上调用list()
,则该生成器中的所有项目都会被消耗,并且生成器将不再产生任何项目(它为“空”)。
答案 2 :(得分:3)
这里有几件事在起作用。
第一件事是,生成器对于给定的每个元素只能运行一次。与列表,元组或具有固定状态的任何其他对象不同,生成器知道__next__
的值是什么,如何知道其后的值,而基本上什么也没有。当您调用next(generator)
时,您将获得下一个值,生成器会计算出一个新的__next__
,它完全失去了您刚刚获得的值的记忆。本质上,发电机不能连续使用。
第二件事是all()
,any()
和list()
在内部如何工作,尤其是相对于生成器而言。 all()
的实现看起来像这样,只是更加复杂:
def all(iterable):
for element in iterable:
if bool(element) is False:
return False
return True
也就是说,all()
函数短路首次发现一个非真实元素时(any()
的作用相同,除了相反的作用)。这是为了节省处理时间-如果仅第一个元素不可接受,为什么还要处理其余的可迭代对象?对于生成器(例如您的最后一个示例),这意味着它会消耗所有元素,直到找到False
。生成器还剩下元素,但是由于已经生成了前两个元素,因此将来它将表现为好像它们根本不存在。
list()
更简单,只需调用next(generator)
直到生成器停止产生值。这使生成器放弃尚未使用的任何值。
所以最后一个例子的解释是
True, False, True, True
all()
,并且在发现虚假值之前,它消耗了生成器的前两个元素。list()
,它将消耗生成器的所有剩余元素(即最后两个)来创建列表。它产生[2, 2]
。