修改嵌套列表时,`for item in list`与`for i in range(len(list))`的差异
问题背景
原地修改一维列表时,索引循环可行,但for-each循环不可行:
可行代码:
l = [1, 2, 3] for i in range(len(l)): l[i] += 1
不可行代码:
l = [1, 2, 3] for num in l: num += 1
一般认为for-each循环中num是列表元素的副本,但嵌套列表的情况却打破了这个认知:
嵌套列表时,以下两段代码均可行:
可行代码1:
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] for i in range(len(l)): for j in range(len(l[i])): l[i][j] += 1
可行代码2:
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] for sublist in l: for i in range(len(sublist)): sublist[i] += 1
而以下代码不可行:
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] for sublist in l: for num in sublist: num += 1
困惑点:如果for-each循环会创建列表元素的副本,那第二段可行代码中的sublist应该是原列表中子列表的副本,修改sublist的元素不应改变原列表l,但实际却生效了,这是为什么?
核心解释:Python的变量是引用,区分可变与不可变类型
首先纠正一个误区:for-each循环里的变量不是元素的副本,而是指向元素的引用。能不能修改原列表,关键看元素本身是可变类型还是不可变类型:
一维列表(元素为不可变类型)
列表中的整数是不可变类型。当执行for num in l时,num是指向列表中某个整数的引用,但整数无法被修改——num += 1本质是创建了一个新的整数对象,让num转而指向这个新对象,原列表里的元素还是原来的整数,所以原列表不会有变化。
而用索引l[i] += 1时,是直接对列表索引i位置的元素进行重新赋值,把新整数放到该位置,所以原列表会被修改。嵌套列表(外层元素为可变类型)
外层列表的元素是子列表(可变类型)。for sublist in l中的sublist是指向原列表中子列表对象的引用,不是副本。当你通过sublist[i] += 1修改这个子列表的元素时,本质是在操作原列表中存储的那个子列表对象,原列表自然会跟着变化。
最后那段两层for-each的代码里,内层的num指向的是子列表里的整数(不可变类型),num +=1同样是创建新整数,不会修改原有的子列表元素,所以原列表没变化。
总结
- 元素为不可变类型(int、str、tuple等):for-each循环里修改变量不会影响原列表,因为变量指向了新对象。
- 元素为可变类型(list、dict等):for-each循环里的变量指向原对象,修改对象内部的元素会影响原列表,因为操作的是同一个对象。
内容的提问来源于stack exchange,提问作者Raymond Li

