我的Python学习笔记(二):浅拷贝和深拷贝
在Python中,对象赋值,拷贝(浅拷贝和深拷贝)之间是有差异的,我们通过下列代码来介绍其区别
一、对象赋值
对象赋值不会复制对象,它只会复制一个对象引用,不会开辟新的内存空间
如下例所示,将test赋值给copytest后,test和copytest的id值相同,test和copytest指向的是同一个list,所以当test的值改变时,copytest的值也相应改变
test = ["test", 18, ["python","java"]] copytest = test print(test) # ['test', 18, ['python', 'java']] print(copytest) # ['test', 18, ['python', 'java']] print(id(test)) # 4559314816 print(id(copytest)) # 4559314816 test[0] = "changetest" test[2].append("css") print(test) # ['changetest', 18, ['python', 'java', 'css']] print(copytest) # ['changetest', 18, ['python', 'java', 'css']] print(id(test)) # 4559314816 print(id(copytest)) # 4559314816
二、拷贝
对于复合对象,如果有时我们不想因为其中一个对象改变而改变另外一个对象的值,我们可以使用Python的copy模块。
copy模块中包含如下两个方法:
copy.copy(x)方法返回x的浅拷贝,copy.deepcopy(x)方法返回x的深拷贝
我们来理解下浅拷贝和深拷贝的区别:
浅拷贝和深拷贝的区别只与复合对象有关系(复合对象就是对象中包含其他对象,例如list和其他实例类)
- 浅拷贝构造了一个新的复合对象,然后(尽可能)把原来的对象引用直接插入到新对象中
- 深拷贝构造了一个新的复合对象,然后递归的将原对象中的内容进行复制,并插入新对象
1、浅拷贝
在下例中,浅拷贝会为copytest开辟新的内存空间,但copytest中的对象仍然是test中的对象引用,不会对其中的对象进行复制
import copy test = ["test", 18, ["python","java"]] copytest = copy.copy(test) print(test) # ['test', 18, ['python', 'java']] print(copytest) # ['test', 18, ['python', 'java']] print(id(test)) # 4484571008 print(id(copytest)) # 4484611320 print [id(x) for x in test] # [4484554208, 140582137648080, 4484569424] print [id(x) for x in copytest] # [4484554208, 140582137648080, 4484569424] test[0] = "changetest" test[2].append("css") print(test) # ['changetest', 18, ['python', 'java', 'css']] print(copytest) # ['test', 18, ['python', 'java', 'css']] print(id(test)) # 4484571008 print(id(copytest)) # 4484611320 print [id(x) for x in test] # [4484554352, 140582137648080, 4484569424] print [id(x) for x in copytest] # [4484554208, 140582137648080, 4484569424]
- test和copytest的id值不同,但copytest并没有将copy内的值重新拷贝一份,而是直接指向copy内的值。
- 当执行test[0] = "changetest”时,test[0]重新指向字符串”changetest”,而copttest[0]仍然指向字符串”test”。
- 当执行test[2].append("css”)时,更改了test[2]指向的list中的值,而test和copytest都指向此list,所以copytest也相应改变了
注:字典的浅拷贝可以使用dict.copy(),list的浅拷贝可以使用slice截取整个list,例如:copied_list = original_list[:]
2、深拷贝:
在下例中,深拷贝会为copytest开辟新的内存空间,并将test中的复合对象进行拷贝,并将其引用赋值给新copytest
import copy test = ["test", 18, ["python","java"]] copytest = copy.deepcopy(test) print(test) # ['test', 18, ['python', 'java']] print(copytest) # ['test', 18, ['python', 'java']] print(id(test)) # 4476972928 print(id(copytest)) # 4477013240 print [id(x) for x in test] # [4476956224, 140706592084752, 4476971344] print [id(x) for x in copytest] # [4476956224, 140706592084752, 4477013312] test[0] = "changetest" test[2].append("css") print(test) # ['changetest', 18, ['python', 'java', 'css']] print(copytest) # ['test', 18, ['python', 'java']] print(id(test)) # 4476972928 print(id(copytest)) # 4477013240 print [id(x) for x in test] # [4476956512, 140706592084752, 4476971344] print [id(x) for x in copytest] # [4476956224, 140706592084752, 4477013312]
- test和copytest的id值不同,copytest将test中的list重新拷贝了一份,并指向重新拷贝的list
- 当执行test[0] = "changetest”时,test[0]重新指向字符串”changetest”,而copttest[0]仍然指向字符串”test”。
- 当执行test[2].append("css”)时,更改了test[2]指向的list的值,但copytest和test指向的并不是同一个list,所以copytest并不受影响
注:深拷贝存在两个问题:一个是递归拷贝可能导致递归循环,另一个是深拷贝会拷贝所有的对象,而有些可以复用的对象也会被重复拷贝,导致拷贝过多的情况