重复赋值指向可迭代对象的变量为何导致Python代码结果异常
问题原因分析
用户提供的代码如下:
import more_itertools as mo def rep(x, n): for i in range(n): yield x xs = [0] for n in [1, 2, 3]: xs = mo.flatten(rep(x, n) for x in xs) print(mo.ilen(xs))
预期逻辑的错误假设
预期输出6的前提是:每次循环都会立刻对xs求值,用当前循环的n值计算新的xs长度:
- 第一次循环n=1:初始xs长度为1,每个元素重复1次,新xs长度=1*1=1
- 第二次循环n=2:每个元素重复2次,新xs长度=1*2=2
- 第三次循环n=3:每个元素重复3次,新xs长度=2*3=6
但这个假设不符合Python的实际运行规则。
核心原因
结果为27是两个Python特性共同作用的结果:
- 迭代器的惰性求值特性
more_itertools.flatten返回的是迭代器对象,赋值时不会立刻执行内部的迭代逻辑,只有最后调用mo.ilen(xs)消费迭代器的时候,才会逐层触发计算。 - 生成器的迟绑定特性
循环中rep(x, n) for x in xs是生成器表达式,它内部引用的变量n不会在生成器定义时就保存当前值,而是等到实际迭代的时候才会读取变量的当前值。
三次循环全部跑完后才会执行mo.ilen对迭代器求值,此时循环变量n已经停留在最后一次迭代的取值3,三层嵌套的迭代器计算时所有层的n取值都是3,最终长度计算为1 * 3 * 3 * 3 = 27,和实际运行结果一致。
修复方案
如果要得到预期的6,只需要在每次循环时强制对迭代器求值,把结果转为实体列表即可:
for n in [1, 2, 3]: xs = list(mo.flatten(rep(x, n) for x in xs))
这样每次循环都会用当时的n值计算出实际的元素列表,不会出现后续变量覆盖的问题。
内容的提问来源于stack exchange,提问作者psarka
相关产品推荐
相关产品推荐

