python - Python 中的 "yield"

标签 python iteration generator yield

我有一个名为 x 的函数,它生成如下所示的生成器:

a = 5
def x():
    global a
    if a == 3:
        raise Exception("Stop")
    a = a - 1
    yield a

然后在 python shell 中我这样调用该函数:

>>> print x().next()
>>> 4
>>> print x().next()
>>> 3
>>> print x().next()
>>> <python-input-112-f3c02bba26c8> in x()
          2     global a
          3     if a == 3:
    ----> 4         raise Exception
          5     a = a - 1
          6     yield a

    Exception:

但是,当我调用该函数并将其分配给一个变量时,它的行为有所不同:

>>> a = 5
>>> b = x()
>>> print b.next()
>>> 4
>>> print b.next()
>>> ----> 1 b.next()
    StopIteration:

这怎么可能?它不应该打印出 3 并在下一次迭代中引发 StopIteration 吗?

PS:我知道第一次调用函数的时候,body并没有运行,只是产生了一个generator。我不明白的一点是,如果我调用并将其分配给一个变量,会发生什么变化?

最佳答案

在您的第一个示例中,您每次都创建了一个生成器:

x().next()

这会从顶部启动生成器,所以第一个语句。当 a == 3 时,会引发异常,否则生成器只会让步并暂停

当您稍后分配生成器时,全局 a5 开始,然后代码从它离开的地方继续直到它结束或遇到另一个 yield 语句,然后 ended。当生成器函数结束时,它会引发 StopIteration

让我们把它分解成几个步骤:

  1. a = 5
  2. 您创建新的生成器并对其调用 .next()。执行以下代码:

    global a
    if a == 3:  # False
        raise Exception("Stop")
    a = a - 1   # a is now 4
    yield a
    

    生成器在最后一行暂停,生成 4

  3. 您创建一个新的生成器并在其上调用 .next()a 开头是4:

    global a
    if a == 3:  # False
        raise Exception("Stop")
    a = a - 1   # a is now 3
    yield a
    

    生成器在最后一行暂停,生成 3

  4. 您创建一个新的生成器并在其上调用 .next()a 开头是3:

    global a
    if a == 3:  # True
        raise Exception("Stop")
    

    引发异常。

  5. 您再次设置a = 5

  6. 您创建了一个新的生成器,将引用存储在 b 中并对其调用 .next()。执行以下代码:

    global a
    if a == 3:  # False
        raise Exception("Stop")
    a = a - 1   # a is now 4
    yield a
    

    生成器在最后一行暂停,生成 4

  7. 您对 b 引用的同一个现有生成器再次调用.next()。代码在暂停点恢复

    该函数此时没有更多代码,并返回。 StopIteration 已引发。

如果您改用循环,您会更清楚地看出区别:

>>> def looping(stop):
...    for i in looping(stop):
...        yield i
...
>>> looping(3).next()
0
>>> looping(3).next()
0

注意每次我创建一个新的生成器时,循环是如何从头开始的。然而,存储一个引用,你会注意到它继续:

>>> stored = looping(3)
>>> stored.next()
0
>>> stored.next()
1
>>> stored.next()
2
>>> stored.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

在循环中,每执行一次yield表达式,代码就会暂停;调用 .next() 继续上一次离开的函数。

StopIteration 异常是完全正常的;这就是生成器传达它们已完成的方式。 for 循环查找此异常以结束循环:

>>> for i in looping(3):
...     print i
... 
0
1
2

关于python - Python 中的 "yield",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18717834/

相关文章:

javascript - 在不同数据选项之间循环迭代对象

scala - Spark 流的迭代算法

python - 我使用生成器表达式有什么问题?

algorithm - 有条件的随 secret 码生成

python - 使用变量作为关键字分配关键字参数的大多数pythonic方式?

python - 使用 Paramiko 通过 SSH 执行重启命令

python - 我可以通过使用循环使迭代变得更容易吗?

python - 进行异步调用时 'yield'在 Tornado 中如何工作?

python - 在 Python 中处理项目脚本中的路径/可执行文件的最佳实践(例如 Django 的 manage.py 或 fabric)

python - 将 Numpy 图像数组居中