python深拷貝和淺拷貝之簡單分析

aaa1111sss發表於2018-08-26

title: python 深拷貝和淺拷貝
tags: python,copy,deepcopy
grammar_cjkRuby: true

python 深拷貝和淺拷貝

python的變數的賦值都是引用
把一個變數賦值給一個變數,不是拷貝這個物件,而是拷貝這個變數的引用

  1. 直接賦值

    傳遞的是這個變數的引用

  2. 淺拷貝

    拷貝的是這個變數的引用,會產生新的物件
    淺拷貝會產生一個新的物件,但是淺拷貝的內容還是原有物件的引用
    看下面的例子

淺拷貝
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型別)而言,是產生了一個新的物件,而不是像淺拷貝一樣,只是原物件元素的引用
因此,原物件的變化,不會引起拷貝物件的變化

總結:
對於淺拷貝,深拷貝來說,如果拷貝物件的元素是不可變型別(或者說不可變物件)
則無論淺拷貝,深拷貝,對原有物件的改變,都不會影響到拷貝的物件的這個元素
因為,不可變型別的更改,其實是新建了一個物件,自然不會影響到原有物件
當被拷貝物件含有可變型別的元素的時候,對於原有物件的這個可變型別的元素的更改:
淺拷貝,會影響到被拷貝物件
深拷貝,不會影響到被拷貝物件,因為深拷貝對於不可變型別的元素,是產生了一個新的物件來複制這個元素的
所以,如果希望複製一份原有物件,不被影響,需要使用深拷貝

相關文章