如何从内存/地址角度证明Python浅拷贝与深拷贝的差异?
如何从内存地址角度区分Python的浅拷贝与深拷贝?
你尝试用id()函数区分浅拷贝(copy.copy)和深拷贝(copy.deepcopy),但当前测试的是不可变元素(整数、字符串),所以没看出二者的核心差异。要从内存地址角度证明差异,得聚焦嵌套的可变元素(比如列表里的子列表),因为浅拷贝和深拷贝的本质区别就体现在对这些嵌套可变对象的处理上。
你的测试代码
# importing copy module import copy # initializing list 1 li1 = [1, 2, [3, 5], 4] # using copy for shallow copy li2 = copy.copy(li1) print("Before Edit:", id(li1[0]) == id(li2[0])) li2[0] = "hi" print(li2, "vs", li1) print("After Edit:", id(li1[0]) == id(li2[0])) print(id(li1[1]) == id(li2[1])) print(id(li1) == id(li2)) print() # using deepcopy for deepcopy li3 = copy.deepcopy(li1) print("Before Edit:", id(li1[0]) == id(li3[0])) li3[0] = "bye" print(li3, "vs", li1) print("After Edit:", id(li1[0]) == id(li3[0])) print(id(li1[1]) == id(li3[1])) print(id(li1) == id(li3))
代码运行输出
Before Edit: True ['hi', 2, [3, 5], 4] vs [1, 2, [3, 5], 4] After Edit: False True False Before Edit: True ['bye', 2, [3, 5], 4] vs [1, 2, [3, 5], 4] After Edit: False True False
核心问题与验证方法
你之前测试的li1[0]、li1[1]都是不可变的整数,Python会对小整数做缓存,所以不管浅拷贝还是深拷贝,这些元素的初始id都会相同。而修改后只是给拷贝后的列表重新赋值了新对象(字符串),和原列表的元素id自然不同,但这体现不出深浅拷贝的差异。
要验证差异,只需测试嵌套的可变元素(比如li1[2]这个子列表):
修改后的测试代码
import copy li1 = [1, 2, [3, 5], 4] # 浅拷贝测试 li2 = copy.copy(li1) # 查看嵌套子列表的内存地址 print("浅拷贝 - 原列表子列表id:", id(li1[2])) print("浅拷贝 - 拷贝后列表子列表id:", id(li2[2])) print("浅拷贝 - 子列表地址是否相同:", id(li1[2]) == id(li2[2])) # 修改拷贝后的子列表元素 li2[2].append(6) print("修改浅拷贝的子列表后:") print("li1:", li1) print("li2:", li2) print() # 深拷贝测试 li3 = copy.deepcopy(li1) # 查看嵌套子列表的内存地址 print("深拷贝 - 原列表子列表id:", id(li1[2])) print("深拷贝 - 拷贝后列表子列表id:", id(li3[2])) print("深拷贝 - 子列表地址是否相同:", id(li1[2]) == id(li3[2])) # 修改深拷贝后的子列表元素 li3[2].append(7) print("修改深拷贝的子列表后:") print("li1:", li1) print("li3:", li3)
预期输出
浅拷贝 - 原列表子列表id: 140697756028992 浅拷贝 - 拷贝后列表子列表id: 140697756028992 浅拷贝 - 子列表地址是否相同: True 修改浅拷贝的子列表后: li1: [1, 2, [3, 5, 6], 4] li2: [1, 2, [3, 5, 6], 4] 深拷贝 - 原列表子列表id: 140697756028992 深拷贝 - 拷贝后列表子列表id: 140697756030272 深拷贝 - 子列表地址是否相同: False 修改深拷贝的子列表后: li1: [1, 2, [3, 5, 6], 4] li3: [1, 2, [3, 5, 6, 7], 4]
原理说明
- 浅拷贝:仅复制外层列表容器,内部的可变元素(如子列表)还是引用原对象的内存地址。所以修改拷贝后的子列表,原列表的子列表也会跟着变,且二者子列表的
id完全相同。 - 深拷贝:会递归复制所有嵌套的可变元素,生成全新的对象。所以深拷贝后的子列表和原列表的子列表内存地址不同,修改其中一个不会影响另一个。
内容的提问来源于stack exchange,提问作者rustlecho
相关产品推荐
相关产品推荐

