jam*_*lak 12 python iterator generator
受我自己答案的启发,我甚至不了解它是如何起作用的,请考虑以下几点:
def has22(nums):
it = iter(nums)
return any(x == 2 == next(it) for x in it)
>>> has22([2, 1, 2])
False
Run Code Online (Sandbox Code Playgroud)
我预计会有一个StopIteration被提升,因为一旦到达2,next(it)就会推进消耗的迭代器.但是,对于生成器表达式,似乎已完全禁用此行为!break一旦发生这种情况,生成器表达似乎立即出现
>>> it = iter([2, 1, 2]); any(x == 2 == next(it) for x in it)
False
>>> it = iter([2, 1, 2]); any([x == 2 == next(it) for x in it])
Traceback (most recent call last):
File "<pyshell#114>", line 1, in <module>
it = iter([2, 1, 2]); any([x == 2 == next(it) for x in it])
StopIteration
>>> def F(nums):
it = iter(nums)
for x in it:
if x == 2 == next(it): return True
>>> F([2, 1, 2])
Traceback (most recent call last):
File "<pyshell#117>", line 1, in <module>
F([2, 1, 2])
File "<pyshell#116>", line 4, in F
if x == 2 == next(it): return True
StopIteration
Run Code Online (Sandbox Code Playgroud)
即便如此!
>>> it=iter([2, 1, 2]); list((next(it), next(it), next(it), next(it))for x in it)
[]
Run Code Online (Sandbox Code Playgroud)
所以我想我的问题是,为什么这个行为启用了生成器表达式?
注意:相同的行为3.x
开发人员认为允许这样做是一个错误,因为它可以掩盖晦涩的错误。因此,PEP 479的接受 意味着这种情况正在消失。
如果您这样做的话from __future__ import generator_stop,在Python 3.5中,默认情况下在Python 3.7中,问题中的示例将失败并带有RuntimeError。您仍然nums可以使用itertools魔术来达到相同的效果(允许不进行预先计算):
from itertools import tee, islice
def has22(nums):
its = tee(nums, 2)
return any(x == y == 2 for x, y in
zip(its[0], islice(its[1], 1, None)))
Run Code Online (Sandbox Code Playgroud)
它最初起作用的原因与发电机的工作方式有关。您可以想到以下for循环:
for a in b:
# do stuff
Run Code Online (Sandbox Code Playgroud)
由于(大致)等同于此:
b = iter(b)
while True:
try:
a = next(b)
except StopIteration:
break
else:
# do stuff
Run Code Online (Sandbox Code Playgroud)
现在,所有示例都将两个 for循环嵌套在一起(一个在生成器表达式中,一个在使用它的函数中),这样,当外部循环执行其next调用时,内部循环将迭代一次。当内循环中的“做东西”是raise StopIteration怎么回事?
>>> def foo(): raise StopIteration
>>> list(foo() for x in range(10))
[]
Run Code Online (Sandbox Code Playgroud)
异常从内部循环传播出去,因为它不在保护范围之内,并被外部循环捕获。在新的行为下,Python将拦截StopIteration即将传播到生成器之外的a,并将其替换为RuntimeError,它将不会被包含的for循环捕获。
这也意味着这样的代码:
def a_generator():
yield 5
raise StopIteration
Run Code Online (Sandbox Code Playgroud)
也将失败,并且邮件列表线程给人的印象是,无论如何这都被认为是错误的形式。正确的方法是:
def a_generator():
yield 5
return
Run Code Online (Sandbox Code Playgroud)
正如您所指出的,列表推导的行为已经有所不同:
>>> [foo() for x in range(10)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
File "<stdin>", line 1, in foo
StopIteration
Run Code Online (Sandbox Code Playgroud)
这在某种程度上讲是一个实现细节泄漏-列表推导不会转换为list对等的生成器表达式的调用,并且显然这样做会导致巨大的性能损失,而这种能力被认为是禁止的。
| 归档时间: |
|
| 查看次数: |
988 次 |
| 最近记录: |