2
g=(i for i in range(4))
for i in [1,10]:
    g=(i+j for j in g)
print(list(g))

请问为什么结果是:[20, 21, 22, 23]
而不是:[11,12,13,14] 呢?

2018-08-15 提问

查看全部 2 个回答

2

生成器的特点是并没有立即执行,而是记住'生产方式',等被调用时再执行.
在您的这个例子中:
g=(i for i in range(4))#此时,如果被list调用,g为会是[0,1,2,3],但没有被调用,只是生成器
以下循环中:
for i in [1,10]:

g=(i+j for j in g)

并不是说,第一次循环i为1时,g就应该为[1,2,3,4],其实g并没有被调用,所有并没有执行,只是记住生成器的值为i+j而已
第二循环时,i为10,同样也没有执行,也仅仅是记住i+j而已
当被print(list(g))命令调用执行时,循环中的变量i的值,已经是10了.
所以最终的g中的每一值,是执行连续执行两次i+j,既i+(i+j)
所以print(list(g))的输出是[20, 21, 22, 23]

还有更神奇的,同样是你的代码:
g=(i for i in range(4))
for i in [1,10]:

g=(i+j for j in g)

for i in g:

print(i)#输出会是20,41,84,171

输出结果和list(g)又不一样,是不是更奇怪?
但是如果改一下循环变量名称:
for k in g:

print(k)#输出20, 21, 22, 23

输出结果就和list(g)一致了.为什么了?

还是因为生产器的'记住生产方式,而没有被立即执行'的原因.
在生成器g中,变量i是一直存在,并没有被释放和回收的,再使用变量i去循环g,i的值就产生混乱了,第一个循环时i还是10,所以第一个值是20,此时,i值已经被赋成了20,所以第二次循环再执行i+(i+j)时,就得到41,同理第三次循环执行i+(i+j)时,i已经是41了,第四次.......所以最终输出20,41,84,171

将循环的变量i换为k后,变量名不再重复,赋值也就不再混乱了,从而和list(g)的结果一致了

推荐答案

2

已采纳

这个要解释起来可能会比较绕,生成器只会在被需要的时候才会执行代码,但这里还需要另一个知识前提,那就是推导式中的变量是临时变量,不会影响到其他变量的,简单来看个例子:

x = 5
a = (x for x in range(3))

print(list(a))    # [0, 1, 2]
print(x)    # 5

不记得是那个python版本处理了这个问题了,在一些比较旧的版本里是没有变量保护机制的,至少我用的是 3.6 版本有。同理,来看下这个和题目中比较接近的:

g=(i for i in range(4))
i = 8
print(list(g))

你猜,这里会打印什么,是 [0, 1, 2, 3] 。生成器中的 i 是受保护的,因此与外部变量 i 无关,它的取值就一定是 0123。

再看一个:

a = 1
g=(a + i for i in range(4))
a = 5
print(list(g))

这里 i 是受保护的,而 a 并没有,由于后续 a 的值是 5,所以打印语句中生成器应该是 <gen 5 + 0, 5 + 1, ...> 的存在。

好,终于能谈谈题目中的代码了。
第一行的 g=(i for i in range(4)) 中,i 是受保护的,所以它的迭代永远都是 0~3

for i in [1,10]:
    g=(i+j for j in g)

这里的 j 也属于受保护的,在第一次循环中,它的值就是 g 初始时的产出 0~3。而这里的 i 不受保护,只是进行变量绑定,在生成器生成数据时才获取其值。

第一次循环后 g 的值:

<gen i+0, i+1, i+2, i+3>

第二次循环, 推导式中j就是生产的值,虽然此时 i=10 ,但i是后续绑定的,所以出生产为 i+0, i+1, i+2,i+3 第二次循环后 g 的值:

<gen i+i+0, i+i+1, i+i+2, i+i+3>

最后打印语句时,i的值是循环体最后一次的值(10),所以打印输出 20, 21, 22, 23

顺便对楼上的:

g=(i for i in range(4))
for i in [1,10]:
    g=(i+j for j in g)
for i in g:
    print(i)    #输出会是20,41,84,171

做个解释。

在第二个循环体开始前,g 生成器是 <gen i+i+0, i+i+1, ..., i+i+3>

for i in g 表明,从 g 生成的数据赋值给 i 。从生成器获得 i+i+0 ,要生成数据了,i得代入此时的值了,此时是什么值呢?没错, i 是上一次循环体最后一个值(10)所以生成 20,将 20 赋值给 i;

第二次循环在从生成器中获取了 i+i+1 ,此时 i 是 20(上一次循环给赋值的,难点在这),所以生成是 41,再次赋值给 i;

第三次循环,取得 i+i+2 此时 i 的值是 41, 所以得到的生成值是 84, 再次赋值给 i;

...等

推广链接