将生成器表达式传递给 any() 和 all()
Passing generator expressions to any() and all()
我只是在 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个值了。
那么,为什么会发生这种情况呢?我确定我遗漏了很多细节。
您遇到的问题是您在生成所有值后使用生成器。
您可以通过运行以下代码验证这一点:
>>> 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
来自pseudo-code:
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()
,生成器中的所有项目都会被消耗掉,并且生成器将不再生成任何项目(它是 "empty")。
这里有几件事在起作用。
首先,生成器可以 运行 为每个给定的元素恰好一次。与列表、元组或任何其他具有固定状态的对象不同,生成器知道 __next__
值是什么,之后如何生成该值,基本上什么都不知道。当你调用 next(generator)
时,你得到了下一个值,生成器计算出一个新的 __next__
,并且它完全失去了你刚刚获得的值的记忆。本质上,生成器不能连续多次使用.
第二件事是 all()
、any()
和 list()
如何在内部工作,尤其是 vis-a-vis 生成器。 all()
的实现看起来像这样,只是更复杂:
def all(iterable):
for element in iterable:
if bool(element) is False:
return False
return True
也就是说,all()
函数 short-circuits 当它第一次找到 non-truthy 元素时(并且 any()
执行同样的事情,除了相反)。这是为了节省处理时间——如果只有第一个元素是不可接受的,为什么还要处理其余的可迭代对象呢?对于生成器(例如您的最后一个示例),这意味着它会消耗所有元素,直到找到 False
。生成器仍然有剩余的元素,但由于它已经生成了前两个元素,它在未来的行为就像它们从未存在过一样。
list()
更简单,只需调用 next(generator)
直到生成器停止生成值。这使得生成器放弃 它尚未消耗的任何值。
所以你最后一个例子的解释是
- 您创建了一个生成器,它将按顺序
True, False, True, True
吐出元素
- 你在那个生成器上调用
all()
,它在终止之前消耗了生成器的前两个元素,发现了一个错误的值。
- 您对该生成器调用
list()
,它会消耗生成器的所有剩余元素(即最后两个元素)以创建一个列表。它产生 [2, 2]
.
我只是在 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个值了。
那么,为什么会发生这种情况呢?我确定我遗漏了很多细节。
您遇到的问题是您在生成所有值后使用生成器。
您可以通过运行以下代码验证这一点:
>>> 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
来自pseudo-code:
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()
,生成器中的所有项目都会被消耗掉,并且生成器将不再生成任何项目(它是 "empty")。
这里有几件事在起作用。
首先,生成器可以 运行 为每个给定的元素恰好一次。与列表、元组或任何其他具有固定状态的对象不同,生成器知道 __next__
值是什么,之后如何生成该值,基本上什么都不知道。当你调用 next(generator)
时,你得到了下一个值,生成器计算出一个新的 __next__
,并且它完全失去了你刚刚获得的值的记忆。本质上,生成器不能连续多次使用.
第二件事是 all()
、any()
和 list()
如何在内部工作,尤其是 vis-a-vis 生成器。 all()
的实现看起来像这样,只是更复杂:
def all(iterable):
for element in iterable:
if bool(element) is False:
return False
return True
也就是说,all()
函数 short-circuits 当它第一次找到 non-truthy 元素时(并且 any()
执行同样的事情,除了相反)。这是为了节省处理时间——如果只有第一个元素是不可接受的,为什么还要处理其余的可迭代对象呢?对于生成器(例如您的最后一个示例),这意味着它会消耗所有元素,直到找到 False
。生成器仍然有剩余的元素,但由于它已经生成了前两个元素,它在未来的行为就像它们从未存在过一样。
list()
更简单,只需调用 next(generator)
直到生成器停止生成值。这使得生成器放弃 它尚未消耗的任何值。
所以你最后一个例子的解释是
- 您创建了一个生成器,它将按顺序
True, False, True, True
吐出元素 - 你在那个生成器上调用
all()
,它在终止之前消耗了生成器的前两个元素,发现了一个错误的值。 - 您对该生成器调用
list()
,它会消耗生成器的所有剩余元素(即最后两个元素)以创建一个列表。它产生[2, 2]
.