今天我已经玩过嵌套的生成器函数,发现了一个有趣的属性/行为,我无法向自己解释。
这可能与对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异常
所以我的问题是:为什么发电机已经到了终点?
答案 0 :(得分:6)
generator
对象是全局的;你永远不会创造一个新的。一旦迭代或关闭,它就会耗尽。
它已关闭,因为您在函数中使用了yield from
。当bar
生成器函数结束时,GeneratorExit
异常将传播到基础生成器(因此foo()
实例),并且它也会关闭。 bar()
结束,因为没有对该对象的引用并且它被清理。
使用
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()
生成器对象。