为何list.copy()对二维列表是引用,一维列表是拷贝?附解决方案
先看你给出的测试代码,运行后确实会发现:修改原二维列表和复制后的二维列表时,两边的元素都会跟着变;但一维列表的修改却各自独立。这个问题的核心,其实是要搞懂Python里浅拷贝和可变/不可变对象的区别,咱们慢慢说:
先搞懂list.copy()的本质
list.copy() 或者常用的切片语法 m[:],实现的都是浅拷贝。简单来说,它只会复制最外层的那个列表容器——对于列表里的元素,它不会重新创建一个新的元素对象,只是复制了元素的引用地址。
一维列表为啥互不影响?
你的一维列表 matrix_1d 里装的都是不可变对象(比如字符串 'I'、'A')。当你执行 matrix2_1d = matrix_1d.copy() 后,新列表里的每个元素都是原字符串的引用。但因为字符串是不可变的,当你做 matrix_1d[0] = 'lol' 时,其实是把原列表第一个位置的引用,从指向原来的 'I' 改成了指向新的字符串 'lol'——而 matrix2_1d 的第一个位置,仍然指向原来的 'I',所以两者的修改不会互相干扰。
二维列表为啥互相影响?
而二维列表 matrix 里的元素是可变对象(也就是另一个列表)。浅拷贝之后,matrix2 里的每个子列表,其实都是原 matrix 里对应子列表的引用——换句话说,matrix[0] 和 matrix2[0] 指向的是同一个列表对象!
所以不管你修改 matrix[0][0] 还是 matrix2[0][1],都是在修改同一个子列表对象本身,自然两边都会看到变化啦。
二维列表的有效复制方案
要让二维列表复制后完全独立,需要实现深拷贝,这里有两种靠谱的方法:
方法1:用copy模块的deepcopy()(通用方案)
这个方法会递归复制所有层级的可变对象,不管你的列表有多少层嵌套都能用:
import copy # 原二维列表 matrix = [['I','A','O','O','I'],['I','A','O','O','I'],['I','A','O','O','I'],['I','A','O','O','I'],['I','A','O','O','I']] # 深拷贝 matrix_deep = copy.deepcopy(matrix) # 修改原列表 matrix[0][0] = 'lol' # 修改复制后的列表 matrix_deep[0][1] = 'kek' # 打印验证,两者完全独立 def printMat(m): for row in m: print(row) printMat(matrix) print("---") printMat(matrix_deep)
方法2:列表推导式+子列表拷贝(针对二维场景更高效)
如果确定你的列表只有两层(二维),可以用列表推导式逐个复制子列表,比deepcopy效率更高:
# 原二维列表 matrix = [['I','A','O','O','I'],['I','A','O','O','I'],['I','A','O','O','I'],['I','A','O','O','I'],['I','A','O','O','I']] # 逐行拷贝子列表 matrix_deep = [row.copy() for row in matrix] # 测试修改 matrix[0][0] = 'lol' matrix_deep[0][1] = 'kek' # 打印验证 def printMat(m): for row in m: print(row) printMat(matrix) print("---") printMat(matrix_deep)
用这两种方法复制后的二维列表,修改其中一个就不会影响另一个了。
内容的提问来源于stack exchange,提问作者Borys Altynnyk

