深浅拷贝
一、示例
import copy a = [11, 22] b = a print(id(a)) # 17812744 print(id(b)) # 17812744 c = copy.deepcopy(a) print(id(a)) # 17812744 print(id(c)) # 17810952 print(a) # [11, 22] print(c) # [11, 22] a.append(33) print(a) # [11, 22, 33] print(c) # [11, 22]
可以看到a、b内存地址是一样,可如下图表示;而c是完完全全拷贝了一份a指向的内容,这就是深拷贝。
二、深浅拷贝
浅拷贝:copy.copy()
import copy a = [11, 22] b = [33, 44] c = [a, b] d = copy.copy(c) print(id(c)) # 17811464 print(id(d)) # 17405960 a.append(55) print(c) # [[11, 22, 55], [33, 44]] print(d) # [[11, 22, 55], [33, 44]]
把c里的东西取出来,然后d指向它,大致如下图所示。
深拷贝:copy.deepcopy()
import copy a = [11, 22] b = [33, 44] c = [a, b] d = copy.deepcopy(c) print(id(c)) # 17942536 print(id(d)) # 17943112 a.append(55) print(c) # [[11, 22, 55], [33, 44]] print(d) # [[11, 22], [33, 44]]
深拷贝:我已经完完全全拷贝过来了,你改你的东西,不关我事。
三、其他
import copy a = [11, 22] b = [33, 44] c = [a, b] d = copy.copy(c) e = copy.deepcopy(c) print(id(c)) # 17680392 print(id(d)) # 17275144 print(id(e)) # 17681736 c.append([55, 66]) print(c) # [[11, 22], [33, 44], [55, 66]] print(d) # [[11, 22], [33, 44]] print(e) # [[11, 22], [33, 44]]
深拷贝、浅拷贝:往c里添加数据,不关我事。
四、坑
如果copy.copy()拷贝的是元祖,那么不会进行浅拷贝。因为元祖是不可变类型,意味着数据一定不能修改,因此用copy.copy()的时候会自动判断,如果拷贝的是元祖,仅仅是指向这个元祖。
import copy a = [11, 22] b = copy.copy(a) print(id(a)) # 17878344 print(id(b)) # 17471496 a = (11, 22) b = copy.copy(a) print(id(a)) # 17454856 print(id(b)) # 17454856 c = a print(id(c)) # 17454856
对于上面这种情况,copy.deepcopy()同理。但是,请看下面代码:
import copy a = [11, 22] b = [33, 44] c = (a, b) d = copy.copy(c) print(id(c)) # 12080904 print(id(d)) # 12080904 e = copy.deepcopy(c) print(id(c)) # 12080904 print(id(e)) # 12095880 print(c) # ([11, 22], [33, 44]) print(e) # ([11, 22], [33, 44]) a.append(55) print(c) # ([11, 22, 55], [33, 44]) print(e) # ([11, 22], [33, 44])
所以, 如果用copy.cpoy()、copy.deepcopy()对一个全部都是不可变类型的数据进行拷贝,那么它们的结果相同,都是指向;如果拷贝的是一个拥有不可变类型的数据,即使元祖是最顶层,那么copy.cpoy()依然是指向,而copy.deepcopy()这时则是深拷贝了。
五、切片也是浅拷贝
a = [11, 22] b = [33, 44] c = [a, b] d = c[:] print(id(c)) # 17750024 print(id(d)) # 17751752 print(id(c[0])) # 17751688 print(id(d[0])) # 17751688 print(a) # [11, 22] a.append(55) print(c) # [[11, 22, 55], [33, 44]] print(d) # [[11, 22, 55], [33, 44]]
d=c[:] 与 d=copy.copy(c) 一样,属于浅拷贝。
六、字典的copy()方法
d = {"name": "pd", "age": 18} c = d.copy() print(id(d)) # 6662152 print(id(c)) # 6662280 # 验证 d = {"name": "pd", "age": 18, "xx": [11, 22]} c = d.copy() d["xx"].append(33) print(d) # {'age': 18, 'xx': [11, 22, 33], 'name': 'pd'} print(c) # {'age': 18, 'xx': [11, 22, 33], 'name': 'pd'} # 这说明了,使用字典copy()方法的时候,字典里指向的value是共享的。
七、应用
import copy a = [11, 22] def func(a): a.append(33) func(a) print(a) # [11, 22, 33] func(copy.deepcopy(a)) print(a) # [11, 22, 33] """ 打印发现没有再次添加33,说明了copy.deepcopy(a)把a复制了一份, 将复制的新列表传入函数内,对其进行操作,而没有影响到函数外的a """ func(a) print(a) # [11, 22, 33, 33]
你想拷贝列表、字典、元祖、对象等等,想拷贝什么就拷贝什么,关键在于,你想干什么?比如你从网上好不容易爬取到了某些数据,忘了保存,就进行测试了。如果你使用了深拷贝的话,这样在测试时即使污染了数据,也不会对源数据产生影响。