何时生成器不是生成器?为何有时调用带yield的函数不返回生成器对象
我有一个函数 (f)
,里面用到了 yield
。我还有一个列表 (E)
。
如果我尝试用 E += f()
,那么 f()
不会返回一个生成器对象,而是直接运行这个函数,这样就会抛出一个异常,因为一些全局变量还没有准备好。
为了避免大家的评论,我知道 E += f()
是错误的,但我只是用这个例子来说明我的问题。如果我做对了,比如 E += [f()]
或者 E.append(f())
,那么 f()
就会返回一个生成器对象,而 f()
里的代码不会被执行,直到调用这个生成器对象的 next
方法。
我的问题是,为什么在错误的情况下 f()
会被执行,抛出的异常为什么不是类似于“函数对象不可迭代”的错误。
2 个回答
好的,当我在输入这个问题的最后一行时,我想我明白了。
E += f()
是在把两个列表合并在一起,这意味着 f() 的结果必须是可以遍历的东西,而且在合并的时候会对这个可遍历的东西进行计算,也就是说会调用 f().next。
E += [f()]
是把生成器对象放在一个可遍历的对象(也就是列表)里面,实际上是这个外面的列表被计算。
E.append(f())
不是在合并列表,而是直接在原来的列表里添加一个元素(如果这个元素有可遍历的特性,那也会被忽略),所以生成器对象不会被计算。
因为表达式 f()
调用了 f
,而 E += f()
会一直运行这个生成器,直到它结束,并把它生成的值添加到 E
中。这并不是“错误”的做法,但确实需要一个能够正常工作的 f
,并且它要能产生有限的值:
>>> E = [1,2,3]
>>> def f():
... yield 4
... yield 5
...
>>> E += f()
>>> E
[1, 2, 3, 4, 5]
相比之下,E += [f()]
会调用 f
,但在执行到代码的第一行之前就会停止,因为此时解释器已经得到了一个足够完整的对象,也就是生成器对象,可以存储在单元素列表 [f()]
中。只有在你之后请求更多元素时,比如从 E[-1]
,代码才会执行到第一个 yield
的地方,然后就会抛出异常。