python 生成器中的代码何时停止执行?

bla*_*air 6 python generator yield-keyword python-3.x

我试图通过构建一个生成器来理解yield语句的行为,该生成器的行为与“枚举”内置函数类似,但我发现根据我如何迭代它而出现不一致。

def enumerate(sequence, start=0):
n = start
for elem in sequence:
    print("Before the 'yield' statement in the generator, n = {}".format(n))
    yield n, elem
    n += 1
    print("After the 'yield' statement in the generator, n = {}".format(n))
Run Code Online (Sandbox Code Playgroud)

我对生成器的理解是,一旦到达yield 语句,代码的执行就会停止,并返回一个值。这与我通过下面的脚本得到的结果相匹配。

a = 'foo'
b = enumerate(a)
n1,v1 = next(b)
print('n1 = {}, v1 = {}\n'.format(n1,v1))
n2,v2 = next(b)
print('n2 = {}, v2 = {}'.format(n2,v2))
Run Code Online (Sandbox Code Playgroud)

在这种情况下,生成器似乎完全停止在yield语句处,并在n+=1处用第二个“next”语句恢复:

Before the 'yield' statement in the generator, n = 0
n1 = 0, v1 = f

After the 'yield' statement in the generator, n = 1
Before the 'yield' statement in the generator, n = 1
n2 = 1, v2 = o
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用下面的 for 循环,生成器似乎不会在yield 语句处停止。

for n,v in enumerate(a[0:1]):
    print('n = {}, v = {}'.format(n,v))
Run Code Online (Sandbox Code Playgroud)

这就是我得到的:

Before the 'yield' statement in the generator, n = 0
n = 0, v = f
After the 'yield' statement in the generator, n = 1
Run Code Online (Sandbox Code Playgroud)

考虑评论进行编辑

我意识到我只迭代了一个元素,但我没想到会看到最后一个“在生成器中的‘yield’语句之后”句子(即使我迭代所有元素,它也会出现。

print('\n\n')
for n,v in enumerate(a):
    print('n = {}, v = {}'.format(n,v))

Before the 'yield' statement in the generator, n = 0
n = 0, v = f
After the 'yield' statement in the generator, n = 1
Before the 'yield' statement in the generator, n = 1
n = 1, v = o
After the 'yield' statement in the generator, n = 2
Before the 'yield' statement in the generator, n = 2
n = 2, v = o
After the 'yield' statement in the generator, n = 3
Run Code Online (Sandbox Code Playgroud)

为什么会出现这种情况?

Mad*_*ist 5

这里的根本问题是,您混淆了仅通过查看生成器就知道何时会耗尽的事实Python 只能通过运行代码来知道的事实。当Python到达yield你认为是最后一个的时候,它实际上并不知道它是最后一个。如果你的生成器看起来像这样怎么办:

def enumeratex(x, start=0):
    for elem in x:
        yield start, x
        start += 1
    yield start, None
Run Code Online (Sandbox Code Playgroud)

在这里,出于无人知晓的原因,最终None元素在主生成器循环之后返回。Python 无法知道生成器已经完成,除非你

  1. 从发电机返回。
  2. 提出错误,在这种情况下一切都会陷入停顿。

在 Python 3.7 之前的版本中,生成器可以引发StopIteration以指示终止。事实上, return 语句相当于raise StopIteration(如果返回None) 或raise StopIteration(return_value)

因此,虽然告诉 Python 结束生成器的确切方式取决于您,但您必须明确说明这一点。Ayield本身并不结束生成器。

长话短说

生成器中循环中的所有代码都将始终运行,即使在生成最后一个值之后也是如此,因为 Python 只能通过实际执行所有代码才能知道它是最后一个值。

  • 编号列表中的第一点不再正确(过去确实如此)。在 Python 3.7(或带有 `from __future__ import Generator_stop` 的 3.5+)中,生成器中未捕获的 `StopIteration` 将被转换为 `RuntimeError`。在新代码中,完成后您应该始终从生成器“返回”(尽管通过运行函数末尾隐式返回“None”仍然可以)。 (2认同)