或许你在其他编程语言比如C 有听说过拷贝分深拷贝和浅拷贝。这两个概念区别就是你复制的是一份对象的引用还是对象本身。今天我们来看一下python的赋值、浅层拷贝和深层拷贝的区别。
一、赋值和拷贝
开始之前,先看一段代码。
代码语言:python代码运行次数:0复制import copy
arr = [ [1, 2, 3], [4, 5, 6] ]
assigned = arr #assign
swallow_copied = copy.copy(arr) #swallow copy
deep_copied = copy.deepcopy(arr) # deep copy
这里看到了我们声明了一个二维数组arr,并使用这个arr赋值了assigned、浅拷贝了swallow_copied和深拷贝了deep_copied。
1.1 对象的内存布局
我们使用python tour来看下三者在object层是如何布置的。
很清楚看到 :
- arr的object对象的引用 赋值给了assigned,也就是assigned的object是等价于arr
- swallow_copied复制了第一层的对象[[][]], 但是就最终的object[1,2,3]和[4,5,6]和arr并无区别
- deep_copied则是在object层完整地拷贝了一份新的object。和arr是完全两码事。
1.2 修改object
接上段代码,接着来修改object。加入了在swallow_copied和deep_copied的操作代码,并和原来的arr进行比较。
代码语言:python代码运行次数:3复制import copy
arr = [ [1, 2, 3], [4, 5, 6] ]
assigned = arr #assign
swallow_copied = copy.copy(arr) #swallow copy
deep_copied = copy.deepcopy(arr) # deep copy
print("before:")
print("arr:", arr)
print("swallow_copied:", swallow_copied)
print("deep_copied:", deep_copied)
swallow_copied.append([7, 8, 9])
swallow_copied[0].append(3.5)
print()
print("after swallow copy:")
print("arr:", arr)
print("swallow_copied:", swallow_copied)
deep_copied.append([7, 8, 9])
deep_copied[0].append(3.5)
print()
print("after deep copy:")
print("arr:", arr)
print("deep_copied:", deep_copied)
使用python tour观察这段代码,你看到区别了吗?
一开始我们的数组长这样:
before:
arr: [ [1, 2, 3], [4,5,6] ]
swallow_copied: [ [1,2,3], [4,5,6] ]
deep_copied:[ [1,2,3], [4,5,6] ]
经过这两行代码进行浅层拷贝之后
代码语言:javascript复制swallow_copied.append([7, 8, 9])
swallow_copied[0].append(3.5)
swallow_copied变成
after swallow copy:
arr: [ [1, 2, 3, 3.5], [4,5,6] ]
swallow_copied: [ [1, 2, 3, 3.5], [4, 5, 6], [7, 8, 9]]
可以看到虽然我只是操作swallow_copied,但是由于swallow_copied和arr共享了swallow_copied[0]的对象,所以这里arr[0]也跟着改变。
然后再经过这两行代码进行深层拷贝之后
代码语言:python代码运行次数:0复制deep_copied.append([7, 8, 9])
deep_copied[0].append(3.5)
deep_copied变成
after deep copy:
arr: [ [1, 2, 3, 3.5], [4,5,6] ]
deep_copied: [ [1, 2, 3, 3.5], [4, 5, 6], [7, 8, 9]]
可以看到操作deep_copied并没有改变arr。
二、mutable和immutable
我们来看下python的赋值copy和deepcopy操作,以及对mutable和immutable类型操作的区别。
先来看一段代码
代码语言:python代码运行次数:1复制x = 5
print(x)
y=x
print(y)
x = 1
print(x)
print(y)
从这段代码的执行结果来看,x =1并没有改变y。也就是说虽然y=x,但是x和y并不是同一个object。
2.1 mutable和非mutable类型
python把object分为两种类型:
- 一种叫immutable,包括integer,float和string。
- 另一种叫mutable,包括list,dict等容器。
python对不同类型也有不同的处理方式
- 这里y=x,也就是说每次处理immutable类型,都会真真实实地创建新的object
- 而处理mutable类型,object还是那个object,只是object里面的values被改变了。你操作了这个object的reference。
2.2 浅拷贝和深拷贝的区别
浅层拷贝和深拷贝只有在操作immutable对象时有区别。
- mutable对象如果持有的是immutable的对象,比如说[1,2,3]这里mutable的[]持有的是immutable的1,2,3。浅层拷贝和深拷贝[1,2,3]是没有区别的
- 如果mutable对象持有的mutable对象。比如说[[1,2,3], [4,5,6]]这里mutable的[[], []] 持有的是mutable的[]。那么mutable的浅层拷贝创建了第一层级的对象,里面的内嵌对象只是保留了reference。但是深拷贝除了会创建第一层级的对象之外,里面的内嵌对象也会创建新的对象。