嵌套的生成器对象上下文

时间:2017-04-25 13:44:08

标签: python generator

今天我已经玩过嵌套的生成器函数,发现了一个有趣的属性/行为,我无法向自己解释。

这可能与对yield from所做的事情缺乏了解有关。但是,现在来问题了:

def foo():
    for iter in range(10):
        yield iter

generator = foo()

def bar():
    yield from generator

print(next(bar()), end=' ')
print(next(bar()), end=' ')
...

所以我除外的输出是0 1 ...

然而,在第一次成功呼叫next后,我得到:     Traceback(最近一次调用最后一次):       文件"",第1行,in     StopIteration异常

所以我的问题是:为什么发电机已经到了终点?

1 个答案:

答案 0 :(得分:6)

generator对象是全局的;你永远不会创造一个新的。一旦迭代或关闭,它就会耗尽。

它已关闭,因为您在函数中使用了yield from。当bar生成器函数结束时,GeneratorExit异常将传播到基础生成器(因此foo()实例),并且它也会关闭。 bar()结束,因为没有对该对象的引用并且它被清理。

来自Yield expressions section

  

使用yield from <expr>时,它会将提供的表达式视为子实例。该子转换器生成的所有值都直接传递给当前生成器方法的调用者。使用send()传入的任何值以及使用throw()传递的任何异常都会传递给底层迭代器(如果它具有适当的方法)。

PEP-380 *Syntax for Delegating to a Subgenerator

  

如果向委托生成器抛出GeneratorExit异常,或者调用委托生成器的close()方法,则调用迭代器的close()方法

详细而言,会发生什么:

  • generator = foo()创建一个新的生成器对象。
  • bar()创造了另一个。但是,除了堆栈之外,没有对此对象的引用。
  • next()从堆栈中获取bar()生成器并将其转发一步。
  • next()会返回,并且bar()生成器没有任何引用,因此会被删除。
  • 删除生成器对象会调用generator.close() method,通过generator.throw()在生成器中引发GeneratorExit
  • yield from通过关闭它将该异常传播到foo()生成器对象。