当生成器用完可产生的值时会发生什么?

New*_*ewb 0 python generator yield-keyword

为了说明这个问题,假设我们有这个简单的生成器:

def firstn(n):
    num = 0
    while num < n:
        yield num
        num += 1

for i in firstn(10):
    print i
Run Code Online (Sandbox Code Playgroud)

这将打印数字 0 到 9。但是如果我们有:

def firstn(n):
    num = 0
    while num < 5 < n:
        yield num
        num += 1

for i in firstn(10):
    print i
Run Code Online (Sandbox Code Playgroud)

(更改在while语句中。)然后它仅打印数字 0 到 4。一旦num >= 5,生成器将不再生成值。

我很好奇的是幕后发生的事情:我使用PythonTutor来逐步执行代码,我的印象是,一旦while语句不再True,函数就会隐式返回None,循环for以某种方式检测到,然后也断了。我使用next内置函数来更仔细地检查这一点:

>>> def firstn(n):
...     num = 0
...     while num < 5 < n:
...         yield num
...         num += 1
... 
>>> 
>>> mygen = firstn(100)
>>> next(mygen)
0
>>> next(mygen)
1
>>> next(mygen)
2
>>> next(mygen)
3
>>> next(mygen)
4
>>> next(mygen)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
Run Code Online (Sandbox Code Playgroud)

这支持了我的理论。我的大问题:它是如何StopIteration工作的,这是否意味着调用具有大值的生成器可以等效于使用其最小终止值调用它?在我们的例子中,for i in firstn(5)for i in firstn(9999999999999)应该是等价的,对吧?

jua*_*aga 5

这并不是很神秘。当生成器用完要生成的值时,它会引发异常StopIteration。您只需要了解 for 循环在 Python 中的工作原理即可。本质上,它等价于下面的代码:

iterator = iter(collection)
while True:
    try:
        x = next(iterator)
        # do something
    except StopIteration as e:
        break
Run Code Online (Sandbox Code Playgroud)

上式等价于:

for x in collection:
    # do something
Run Code Online (Sandbox Code Playgroud)