yield_atom ::= "(" ")" yield_from ::= "yield" "from" yield_expression ::= "yield" |
yield 表达式在定义 函数或 函数时才会用到因此只能在函数定义的内部使用。 在一个函数体内使用 yield 表达式会使这个函数变成一个生成器函数,而在一个 函数的内部使用它则会让这个协程函数变成一个异步生成器函数。 例如:
由于它们会对外层作用域造成附带影响, 表达式不被允许作为用于实现推导式和生成器表达式的隐式定义作用域的一部分。
下面是对生成器函数的描述,异步生成器函数会在 一节中单独介绍。
当一个生成器函数被调用时,它将返回一个名为生成器的迭代器。 然后这个生成器将控制生成器函数的执行。 执行过程会在这个生成器的某个方法被调用时开始。 这时,函数会执行到第一个 yield 表达式,在那里它将再次被挂起,向生成器的调用方返回 的值,或者如果 被省略则返回 。 所谓的挂起,就是说所有局部状态都会被保留,包括局部变量的当前绑定、指令指针、内部求值栈及任何异常处理等等。 当通过调用生成器的某个方法恢复执行时,这个函数的运行就与 yield 表达式只是一个外部调用的情况完全一样。 在恢复执行后 yield 表达式的值取决于恢复执行所调用的方法。 如果是用 (一般是通过 或者 内置函数) 则结果为 。 在其他情况下,如果是用 ,则结果将为传给该方法的值。
所有这些使生成器函数与协程非常相似;它们 yield 多次,它们具有多个入口点,并且它们的执行可以被挂起。唯一的区别是生成器函数不能控制在它在 yield 后交给哪里继续执行;控制权总是转移到生成器的调用者。
在 结构中的任何位置都允许yield表达式。如果生成器在(因为引用计数到零或是因为被垃圾回收)销毁之前没有恢复执行,将调用生成器-迭代器的 方法. close 方法允许任何挂起的 子句执行。
当使用 时,所提供的表达式必须是一个可迭代对象。 迭代该可迭代对象所产生的值会被直接传递给当前生成器方法的调用者。 任何通过 传入的值以及任何通过 传入的异常如果有适当的方法则会被传给下层迭代器。 如果不是这种情况,那么 将引发 或 ,而 将立即引发所转入的异常。
当下层迭代器完成时,被引发的 实例的 属性会成为 yield 表达式的值。 它可以在引发 时被显式地设置,也可以在子迭代器是一个生成器时自动地设置(通过从子生成器返回一个值)。
当yield表达式是赋值语句右侧的唯一表达式时,括号可以省略。
生成器-迭代器的方法
这个子小节描述了生成器迭代器的方法。 它们可被用于控制生成器函数的执行。
请注意在生成器已经在执行时调用以下任何方法都会引发 异常。
例子
这里是一个简单的例子,演示了生成器和生成器函数的行为:
对于 的例子, 参见“Python 有什么新变化”中的 。
异步生成器函数
在一个使用 定义的函数或方法中出现的 yield 表达式会进一步将该函数定义为一个 函数。
当一个异步生成器函数被调用时,它会返回一个名为异步生成器对象的异步迭代器。 此对象将在之后控制该生成器函数的执行。 异步生成器对象通常被用在协程函数的 语句中,类似于在 语句中使用生成器对象。
调用某个异步生成器的方法将返回一个 对象,执行会在此对象被等待时启动。 到那时,将执行至第一个 yield 表达式,在那里它会再次挂起,将 的值返回给等待中的协程。 与生成器一样,挂起意味着所有局部状态会被保留,包括局部变量的当前绑定、指令指针、内部求值栈以及任何异常处理的状态。 当执行在等待异步生成器的方法返回下一个对象后恢复时,该函数可以从原状态继续执行,就仿佛 yield 表达式只是另一个外部调用那样。 恢复执行后 yield 表达式的值取决于恢复执行所用的方法。 如果是使用 则结果为 。 否则的话,如果是使用 ,则结果将是传递给该方法的值。
如果一个异步生成器恰好因 、调用方任务被取消,或是其他异常而提前退出,生成器的异步清理代码将会运行并可能引发异常或访问意外上下文中的上下文变量 -- 也许是在它所依赖的任务的生命周期之后,或是在异步生成器垃圾回收钩子被调用时的事件循环关闭期间。 为了防止这种情况,调用方必须通过调用 方法来显式地关闭异步生成器以终结生成器并最终从事件循环中将其分离。
在异步生成器函数中,yield 表达式允许出现在 结构的任何位置。但是,如果一个异步生成器在其被终结(由于引用计数达到零或被作为垃圾回收)之前未被恢复,则 结构中的 yield 表达式可能导致挂起的 子句执行失败。在此情况下,应由运行该异步生成器的事件循环或任务调度器来负责调用异步生成器-迭代器的 方法并运行所返回的协程对象,从而允许任何挂起的 子句得以执行。
为了能在事件循环终结时执行最终化处理,事件循环应当定义一个 终结器 函数,它接受一个异步生成器迭代器并将调用 且执行该协程。 这个 终结器 可以通过调用 来注册。 当首次迭代时,异步生成器迭代器将保存已注册的 终结器 以便在最终化时调用。 有关 终结器 方法的参考示例请查看在 的中的 实现。
表达式如果在异步生成器函数中使用会引发语法错误。