Python中yield生成器对象迭代行为异常的原因解析
生成器迭代行为的疑问与解释
我之前了解到包含yield语句的函数是生成器函数,调用时返回生成器对象而非直接执行函数体,仅在迭代时才会执行函数体。我能理解前两段代码的执行结果,但运行以下代码时输出不符合预期:
def generatorFunc(): global numList numList = range(8) for i in numList: print(f"id of {i} is {id(i)}") yield i*i mygenerator = generatorFunc() print(mygenerator) for i in mygenerator: print(f"value of i is {i}") print(f"the list is {list(mygenerator)}") list2 = ([i*i for i in mygenerator]) print(list2)
代码输出为:
<generator object generatorFunc at 0x000001E9692A0CF0> id of 0 is 140733860812528 value of i is 0 id of 1 is 140733860812560 id of 2 is 140733860812592 id of 3 is 140733860812624 id of 4 is 140733860812656 id of 5 is 140733860812688 id of 6 is 140733860812720 id of 7 is 140733860812752 the list is [1, 4, 9, 16, 25, 36, 49] []
我无法理解:为什么print(f"value of i is {i}")仅执行一次,print(f"the list is {list(mygenerator)}")最后才打印?是不是生成器对象被迭代一次后就会耗尽变为空?
问题解释
没错,生成器对象是一次性的迭代器,一旦迭代到末尾就会耗尽,后续再迭代不会返回任何元素。咱们一步步拆解代码的执行流程:
- 调用
generatorFunc()得到生成器对象mygenerator,此时函数体并未执行,所以先打印出生成器对象的标识<generator object generatorFunc at ...>。 - 进入外层
for i in mygenerator循环:- 第一次迭代生成器,开始执行
generatorFunc的函数体:初始化numList为range(8),进入内部for循环取第一个元素0,打印它的id,然后yield 0*0,这个值被赋值给循环变量i,因此执行print(f"value of i is 0")。 - 接下来执行
print(f"the list is {list(mygenerator)}"):list()会一次性迭代生成器的剩余所有元素。此时生成器从之前暂停的位置(i=1)继续执行,依次打印i=1到7的id,同时yield出1、4、9…49这些值,这些值被收集成列表[1,4,9,16,25,36,49],所以这行print要等所有id打印完成、列表生成后,才会输出内容。 - 至此,生成器已经被
list()完全迭代耗尽,处于空状态。
- 第一次迭代生成器,开始执行
- 回到外层的
for i in mygenerator循环,由于生成器已经耗尽,循环无法进行下一次迭代,所以print(f"value of i is {i}")只执行了一次。 - 最后执行
list2 = ([i*i for i in mygenerator]),此时生成器已经没有元素可以迭代,所以得到的是空列表,打印出[]。
内容的提问来源于stack exchange,提问作者Vijith Kumar V
相关产品推荐
相关产品推荐

