python深拷贝和浅拷贝之简单分析


title: python 深拷贝和浅拷贝
tags: python,copy,deepcopy
grammar_cjkRuby: true

python 深拷贝和浅拷贝

python的变量的赋值都是引用
把一个变量赋值给一个变量,不是拷贝这个对象,而是拷贝这个变量的引用

  1. 直接赋值

传递的是这个变量的引用

  1. 浅拷贝

拷贝的是这个变量的引用,会产生新的对象
浅拷贝会产生一个新的对象,但是浅拷贝的内容还是原有对象的引用
看下面的例子

浅拷贝
import copy
a = [1, 2, 3, 4 ,5]
b = copy.copy(a)
print id(a)
140637017675968
print id(b)
140637017452416
print [id(item) for item in a]
[30363992, 30363968, 30363944, 30363920, 30363896]
print [id(item) for item in b] 
[30363992, 30363968, 30363944, 30363920, 30363896]

我们可以看到,copy.copy这个浅拷贝的操作,产生了一个新的对象
因此,id(a) ,id(b)是不同的
但是,浅拷贝对于所拷贝对象中的各个元素,只会使用这些元素的引用,并不会再继续产生新的对象
所以,两者各个元素的id是相同的

再看个浅拷贝的例子

import copy
a = [1, 2, 3, 4 ,5]
b = copy.copy(a)
print id(a)
140337638905904
print id(b)
140337638904824
a.append(6)
print a
[1, 2, 3, 4, 5, 6]
print id(a)
140337638905904
print b
[1, 2, 3, 4, 5]
print id(b)
140337638904824

可以看到,因为浅拷贝也是创建了一个新的对象,所以a 和 a的浅拷贝b,是两个不同的对象
所以给对象a添加元素,并不会对b产生影响

再来看一个浅拷贝的例子 这个浅拷贝的例子对于其拷贝的对象也产生了影响,为什么呢?

import copy
 a = [1, 2, 3, [1]]
print id(a)
140063950885808
b = copy.copy(a)
print id(b)
140063949633512
print [id(item) for item in a]
[29266264, 29266240, 29266216, 140063949688416]
print [id(item) for item in b]
[29266264, 29266240, 29266216, 140063949688416]
a[3].append(2)
print a
[1, 2, 3, [1, 2]]
 print b
[1, 2, 3, [1, 2]]

可以看到,这个例子中,a对象和其拷贝产生的对象b
首先,浅拷贝,新建了一个对象b
a 和 b是两个不同的对象
由于浅拷贝,新建的对象b的各个元素其实是原对象a的各个元素的引用(这是和上一个例子不同的地方,上一个例子是原有对象新增了一个元素)
这个例子是原有对象对于其最后一个元素(也是列表类型)新增了一个元素
因为 a 和b 两个对象的各个元素其实是一样的(b的各个元素其实是a的各个元素的引用),所以a 的某个元素的变化会让b一样的变化

我们再看一个浅拷贝的例子

import copy
a = [1, 2, 3, [1]]
print id(a)
140063950885808
b = copy.copy(a)
print id(b)
140063949633512
print [id(item) for item in a]
[29266264, 29266240, 29266216, 140063949688416]
print [id(item) for item in b]
[29266264, 29266240, 29266216, 140063949688416]
a[3].append(2)
print a
[1, 2, 3, [1, 2]]
print b
[1, 2, 3, [1, 2]]
a[0] = 10
a
[10, 2, 3, [1, 2]]
b
[1, 2, 3, [1, 2]]
print [id(item) for item in a]
[29266048, 29266240, 29266216, 140063949688416]
print [id(item) for item in b]
[29266264, 29266240, 29266216, 140063949688416]

前面的操作都没毛病,怎么最后对于对象a的第一个元素的操作让a变化了,却没有影响到b呢?
这是因为操作的a的第一个元素是int类型,这是不可变对象,对于不可变对象的重新赋值本质上是新建一个新的对象
所以,其实这样操作后a的第一个元素对应的对象已经不是原来的对象了,当然不会影响到b

深拷贝
import copy
a = [1, 2, 3, 4, [1]]
b = copy.deepcopy(a)
print [id(item) for item in a]
[40378712, 40378688, 40378664, 40378640, 140313123501048]
print [id(item) for item in b]
[40378712, 40378688, 40378664, 40378640, 140313122753568]
a[4].append(2)
a
[1, 2, 3, 4, [1, 2]]
b
[1, 2, 3, 4, [1]]

在上面,我们可以看到,深拷贝,对于a 的 a[4] 即一个可变对象(list类型)而言,是产生了一个新的对象,而不是像浅拷贝一样,只是原对象元素的引用
因此,原对象的变化,不会引起拷贝对象的变化

总结:
对于浅拷贝,深拷贝来说,如果拷贝对象的元素是不可变类型(或者说不可变对象)
则无论浅拷贝,深拷贝,对原有对象的改变,都不会影响到拷贝的对象的这个元素
因为,不可变类型的更改,其实是新建了一个对象,自然不会影响到原有对象
当被拷贝对象含有可变类型的元素的时候,对于原有对象的这个可变类型的元素的更改:
浅拷贝,会影响到被拷贝对象
深拷贝,不会影响到被拷贝对象,因为深拷贝对于不可变类型的元素,是产生了一个新的对象来复制这个元素的
所以,如果希望复制一份原有对象,不被影响,需要使用深拷贝

原文地址:https://www.cnblogs.com/haozike/p/python_copy_and_deepcopy.html