当你调用一个包含 yield 的函数时会发生什么?

What happens when you invoke a function that contains yield?

我阅读了here下面的例子:

>>> def double_inputs():
...     while True:      # Line 1
...         x = yield    # Line 2
...         yield x * 2  # Line 3
...
>>> gen = double_inputs()
>>> next(gen)       # Run up to the first yield
>>> gen.send(10)    # goes into 'x' variable

如果我对上面的理解正确的话,这似乎意味着 Python 实际上在函数体中等待直到 next(gen) 到 "run up to" 到 Line 2。换句话说,解释器 不会 开始执行函数体,直到我们调用 next.

  1. 这真的正确吗?
  2. 据我所知,Python 不进行 AOT 编译,而且 "look ahead" 除了解析代码并确保其有效 Python 外,它的作用也不大。这个对吗?
  3. 如果以上是真的,Python 怎么会知道当我调用 double_inputs() 它需要等到我调用 next(gen) 才进入循环 while True

正确。调用 double_inputs 永远不会执行任何代码;它只是 returns 一个 generator 对象。 yield 表达式在正文中的存在,在 def 语句被 解析时发现 ,改变了 def 语句的语义以创建一个generator 对象而不是 function 对象。

函数包含 yield 是一个生成器。

当您调用 gen = double_inputs() 时,您会得到一个生成器实例作为结果。您需要通过调用 next.

来使用此生成器

所以对于你的第一个问题,这是真的。当您第一次调用 next.

时,它运行第 1、2、3 行

关于你的第二个问题,我不太明白你的意思。当你定义函数的时候,Python知道你在定义什么,运行它不需要向前看。

第三个问题,关键是yield关键词。

Generator-function 是 de iure 函数,但 de facto 它是一个迭代器,即 class(实现了 __next__()__iter()__ 和一些其他方法。)

换句话说,它是一个class 伪装成一个函数。

这意味着,“调用”这个函数实际上是创建这个class的实例,并解释了为什么“被调用函数”最初没有。这是您第 3 个问题的答案。


您第 1st 个问题的答案令人惊讶 没有

实例总是等待调用它的方法,__next__()方法(通过调用next()内置函数间接启动)不是生成器的唯一方法。其他方法是 .send(),您可以使用 gen.send(None) 而不是 next(gen)


您的第 2 个问题的答案是。 Python 解释器绝不是“向前看”,没有例外,包括你的

... except for parsing the code and making sure it's valid Python.

或者这个问题的答案是,如果你的意思是“解析直到下一个命令”。 ;-)