我的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并不受影响

 

注:深拷贝存在两个问题:一个是递归拷贝可能导致递归循环,另一个是深拷贝会拷贝所有的对象,而有些可以复用的对象也会被重复拷贝,导致拷贝过多的情况

 

posted @ 2018-01-07 19:32  金布  阅读(485)  评论(0编辑  收藏  举报