我的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)) #
print(id(copytest)) # test[0] = "changetest"
test[2].append("css")
print(test) # ['changetest', 18, ['python', 'java', 'css']]
print(copytest) # ['changetest', 18, ['python', 'java', 'css']]
print(id(test)) #
print(id(copytest)) #

二、拷贝

对于复合对象,如果有时我们不想因为其中一个对象改变而改变另外一个对象的值,我们可以使用Python的copy模块。

copy模块中包含如下两个方法:

我的Python学习笔记(二):浅拷贝和深拷贝

我的Python学习笔记(二):浅拷贝和深拷贝copy.copy(x)方法返回x的浅拷贝,copy.deepcopy(x)方法返回x的深拷贝

我们来理解下浅拷贝和深拷贝的区别:

我的Python学习笔记(二):浅拷贝和深拷贝

浅拷贝和深拷贝的区别只与复合对象有关系(复合对象就是对象中包含其他对象,例如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)) #
print(id(copytest)) #
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)) #
print(id(copytest)) #
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)) #
print(id(copytest)) #
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)) #
print(id(copytest)) #
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并不受影响

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

我的Python学习笔记(二):浅拷贝和深拷贝

我的Python学习笔记(二):浅拷贝和深拷贝

上一篇:python gevent自动挡的协程切换。


下一篇:python学习笔记(二)之python简单实践