Python中for循环内为集合添加元素时循环终止的原因是什么
现象本质原因
两个案例的运行差异和列表、集合本身的迭代规则没有关系,核心区别是循环内对容器的操作性质完全不同:
- 列表无限循环的案例里,
append()是原地修改操作:for循环启动迭代时,就已经和初始创建的那个列表对象绑定了迭代器,整个循环过程中你调用append始终是在修改这同一个原对象,迭代器能实时读到列表新增的元素,自然会一直运行不会终止。 - 集合循环终止的案例里,
union()不是原地操作:这个方法不会修改原有集合,只会返回一个合并后的全新集合对象。你在循环里写myset = myset.union([num+1]),本质是把myset这个变量名重新绑定到新生成的集合上,但for循环持有的迭代器从始至终都绑定在最开始的那个原始集合(也就是初始的{0})上,完全感知不到变量名后续绑定的新对象。
验证方式
你只要把集合案例里的非原地操作换成集合的原地添加方法add(),集合遍历也会出现无限循环的效果,和列表的表现完全一致:
myset = {0} for num in myset: myset.add(num+1) # 代码会一直运行,永远到不了下面的打印行 print(myset)
反过来,如果你把列表案例里的原地append换成生成新列表的操作,就算是列表,循环也会正常终止,和你最开始写的集合案例表现一模一样:
mylist = [0] for num in mylist: mylist = mylist + [num+1] # 列表+操作会生成全新列表,不修改原对象 print(mylist) # 最终输出[0,1]
执行流程拆解
你最开始写的集合案例,实际运行逻辑是这样的:
- 初始创建集合对象A,内容为
{0},变量myset指向对象A - for循环启动,对
in后的myset(也就是对象A)调用iter()拿到专属迭代器,后续迭代全程只和这个迭代器、以及迭代器绑定的对象A交互 - 第一次迭代从迭代器取出元素0,进入循环体:调用A的union方法生成全新的集合对象B(内容
{0,1}),再把变量myset重新绑定到对象B——这一步从头到尾没有修改过对象A的内容,对象A始终是{0} - 迭代器尝试从对象A中取下一个元素,发现对象A已经遍历完成,直接抛出迭代终止信号,循环结束
- 最后打印的
myset是变量当前绑定的对象B,所以输出{0, 1}
核心规则:Python的for循环在启动瞬间就已经确定了要迭代的对象,循环过程中你把迭代用的变量重新绑定到其他任何对象,都不会影响已经启动的迭代器的行为。
内容的提问来源于stack exchange,提问作者PeriodicParticle
相关产品推荐
相关产品推荐

