title: python 深拷貝和淺拷貝
tags: python,copy,deepcopy
grammar_cjkRuby: true
—
python 深拷貝和淺拷貝
python的變數的賦值都是引用
把一個變數賦值給一個變數,不是拷貝這個物件,而是拷貝這個變數的引用
-
直接賦值
傳遞的是這個變數的引用
-
淺拷貝
拷貝的是這個變數的引用,會產生新的物件
淺拷貝會產生一個新的物件,但是淺拷貝的內容還是原有物件的引用
看下面的例子
淺拷貝
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型別)而言,是產生了一個新的物件,而不是像淺拷貝一樣,只是原物件元素的引用
因此,原物件的變化,不會引起拷貝物件的變化
總結:
對於淺拷貝,深拷貝來說,如果拷貝物件的元素是不可變型別(或者說不可變物件)
則無論淺拷貝,深拷貝,對原有物件的改變,都不會影響到拷貝的物件的這個元素
因為,不可變型別的更改,其實是新建了一個物件,自然不會影響到原有物件
當被拷貝物件含有可變型別的元素的時候,對於原有物件的這個可變型別的元素的更改:
淺拷貝,會影響到被拷貝物件
深拷貝,不會影響到被拷貝物件,因為深拷貝對於不可變型別的元素,是產生了一個新的物件來複制這個元素的
所以,如果希望複製一份原有物件,不被影響,需要使用深拷貝