浅拷贝与深拷贝

对象赋值实际上是只是对象的引用,如果想要复制某个对象,需要通过完全切片或者工厂函数以及使用copy模块的方法来完成。但是这三种方法都是所谓的“浅拷贝”。首先明确,“浅拷贝”和“深拷贝”的概念仅仅是针对容器类型的对象,因为非容器类型没有拷贝这一说法。他们的区别在于:

  • 浅拷贝将创建一个新的容器对象,然后把对原容器中元素的引用插入到新容器中。
  • 深拷贝将创建一个新的容器对象,然后递归地把原容器中元素的“拷贝”插入到新容器中。


下面是浅拷贝:
[cc lang=”python”]
>>> person = [‘name’, [‘savings’, 100.0]]
>>> husband = person[:]
>>> wife = person[:]
>>> [id(x) for x in person, husband, wife]
[11691272, 16181008, 16181408]
>>> [id(x) for x in person]
[11722272, 16057728]
>>> [id(x) for x in husband]
[11722272, 16057728]
>>> [id(x) for x in wife]
[11722272, 16057728]
[/cc]
用id函数可以看到,通过完全切片操作,创建了两个新的容器husband和wife,他们的id都和原来的容器person不同。但是,新容器中的元素还是与原容器的元素相同,因为浅拷贝只是插入了对原容器元素的引用而已。

使用copy.deepcopy进行深拷贝如下:
[cc lang=”python”]
>>> person = [‘name’, [‘savings’, 100.0]]
>>> husband = person
>>> import copy
>>> wife = copy.deepcopy(person)
>>> [id(x) for x in person, husband, wife]
[11691272, 11691272, 16182448]
>>> [id(x) for x in wife]
[11722272, 16181968]
>>> [id(x) for x in husband]
[11722272, 16057728]
[/cc]
这次不同的是,husband[1]和wife[1]这两个元素的id不再相同。也许会有疑问,既然是深拷贝,那么husband[0]和wife[0]不也应该不同么?这是因为第一个元素是一个字符串,而字符串是不可变类型,没有拷贝的说法,所以两个对象的第一个元素都是指向字符串”name”的引用。同样也可以解释husband[1]和wife[1]这两个列表的情况。
[cc lang=”python”]
>>> [id(x) for x in wife[1]]
[16176320, 14630992]
>>> [id(x) for x in husband[1]]
[16176320, 14630992]
[/cc]
下面分别比较一下浅拷贝和深拷贝后修改容器中元素的不同。
[cc lang=”python”]
>>> person = [‘name’, [‘savings’, 100.0]]
>>> husband = person[:]
>>> wife = person[:]
>>> husband[0] = ‘joe’
>>> wife[0] = ‘jane’
>>> husband, wife
([‘joe’, [‘savings’, 100.0]], [‘jane’, [‘savings’, 100.0]])
>>> husband[1][1] = 50.0
>>> husband, wife
([‘joe’, [‘savings’, 50.0]], [‘jane’, [‘savings’, 50.0]])
[/cc]
[cc lang=”python”]
>>> person = [‘name’, [‘savings’, 100.0]]
>>> husband = person
>>> wife = copy.deepcopy(person)
>>> husband[0] = ‘joe’
>>> wife[0] = ‘jane’
>>> husband, wife
([‘joe’, [‘savings’, 100.0]], [‘jane’, [‘savings’, 100.0]])
>>> husband[1][1] = 50.0
>>> husband, wife
([‘joe’, [‘savings’, 50.0]], [‘jane’, [‘savings’, 100.0]])
[/cc]
因为浅拷贝只是复制了引用,husband[1]与wife[1]其实是指向同一对象的引用,修改其中一个,另外一个会同样变化。而在深拷贝中,husband[1]和wife[1]则是不同的对象,所以修改其中一个不会影响另外一个。

最后还有一点,如果元组变量只包含原子类型的对象,即便使用深拷贝也只能得到浅拷贝的结果。

发表评论

电子邮件地址不会被公开。 必填项已用*标注