直接賦值: 物件的引用,也就是給物件起別名
淺拷貝: 拷貝父物件,但是不會拷貝物件的內部的子物件。
深拷貝: 拷貝父物件. 以及其內部的子物件
在之前的文章中,提到可變物件和不可變物件,接下來也是以這兩者的區別進行展開
直接賦值
對於可變物件和不可變物件,將一個變數直接賦值給另外一個變數,兩者 id 值一致,其實本質上是將變數量繫結到物件的過程.
>>> a=1
>>> b=a
>>> id(a) == id(b)
True
>>> c="string"
>>> d=c
>>> id(c) == id(d)
True
>>> e=[1,2,3]
>>> f=e
>>> id(e)==id(f)
True
關於修改新變數的值,對原有變數會產生的影響,在可變物件和不可變物件 中也做了講述,這裡通過幾個例子,重新溫習一下
不可變物件
>>> x=1
>>> y=x
>>> id(x)==id(y)
True
>>> id(1)==id(y)
True
>>>>>> id(x)
1500143776
>>> y=y+1
>>> y
2
>>> x
1
>>> id(x)==id(y)
False
>>> id(y)
1500143808
>>> id(x)
1500143776
對於不可變物件,修改賦值後的新變數,不會對原有變數造成任何影響.為什麼出現這種現象呢?因為不可變物件一旦建立之後就不允許被改變.後面對 y
進行的操作,其實是重新建立一個物件並繫結的結果:
可變物件
>>> m=[1,2,3]
>>> n=m
>>> id(n)==id(m)
True
>>> id(m)
1772066764488
>>> id(n[0])
1772066764656
>>> n[0]=4
>>> n
[4, 2, 3]
>>> m
[4, 2, 3]
>>> id(n)==id(m)
True
>>> id(m)
1772066764488
對於可變物件,修改賦值後的變數,會對原有的變數造成影響,會導致其 value
值的改變,但是其 id
值保持不變
從上圖不難看出,這個時候的 id(n[0])
的值,和未修改前的 id
值應該不一樣,可以輸出看一下
>>>id(n[0])
1772066764752 # 最初沒有修改前是 1772066764656
n[0]
修改前後為什麼 id 值出現改變呢? 首先需要明確一點 n[0]
繫結的是一個不可變物件,在文章的最初提到,不可變物件一旦建立就不允許修改.顯然對 n[0]
進行修改,不能在繫結物件的記憶體上進行修改,那如何實現重新賦值呢?只能建立一個新的物件 4
,然後將 n[0]
繫結到新的物件
淺拷貝和深拷貝
先看一下官方文件的定義
The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or
class instances).
A shallow copy constructs a new compound object and then (to the
extent possible) inserts the same objects into it that the
original contains.
A deep copy constructs a new compound object and then, recursively,inserts copies into it of the objects found in the original.
從文件中不難看出,上面提到深拷貝和淺拷貝兩者區別在於在複合物件,那接下來也只討論複合物件.
淺拷貝
注意到官方文件也提到對淺拷貝和深拷貝的定義,從上文中不難看出,淺拷貝構建一個複合物件,然後將原有複合物件包含的物件插入到新的複合物件中
從上圖不難看出,淺拷貝後,新複合物件包含的物件(可變或者不可變)的 id 值和原有物件包含的物件的 id 值相同
看一下具體例子:
>>> import copy
>>> a=[1,2,[3,4]]
>>> b=copy.copy(a)
>>> id(b[0])==id(a[0])
True
>>> id(b[2])==id(a[2])
True
>>> id(b[2][0])==id(a[2][0])
True
現在讓我們試著修改一下淺拷貝後的 b
的值,在修改前,可以先思考一下,如果修改 b[0]
可能會發生什麼?
由於 b[0] = 1
,很顯然 1
屬於不可變物件,那麼根據對不可變變數修改的規則,則 b[0]
會繫結到新的變數上,而 a[0]
的由於沒有修改,則保持不變,真的是這樣嗎?讓我們驗證一下
>>> b[0]=5
>>> b
[5, 2, [3, 4]]
>>> a
[1, 2, [3, 4]]
接下來我們要嘗試修改一下 b[2]
,由於 b[2]
繫結的物件是 list
,屬於可變物件,按照上面說的可變物件修改的規則,則修改後的 b[2]
的 id
值保持不變,但是其 value
值會發生改變. 同樣的讓我們通過例子驗證一下
>>> id(b[2])
4300618568
>>> b[2][0]=6
>>> id(b[2])
4300618568
>>> b
[5, 2, [6, 4]]
>>> a
[1, 2, [6, 4]]
由於 b[2]
和 a[2]
繫結同一個可變物件,很顯然對 b[2]
的修改同樣會對映到 a[2]
上
深拷貝
深拷貝構建一個複合物件,然後遞迴的將原有複合包含的物件的副本插入到新的複合物件中
若上圖所示,深拷貝後,新的複合物件包含的物件,若物件為不可變物件,則 id 值保持不變,若物件為可變物件,則 id 值發生改變
看一個例子:
>>> import copy
>>> a=[1,2,[3,4]]
>>> b=copy.deepcopy(a)
>>> id(b[0])==id(a[0])
True
>>> id(b[2])==id(a[0])
False
>>> id(b[2][0])==id(a[2][0])
True
接下來讓我們修改一下變數 b
,這裡就不在修改不可變物件 b[0]
和 b[1]
了,因為結果很明顯,對 a 不會產生任何影響,我們來修改 b[2]
,那麼修改 b[2]
會對 a[2]
產生影響嗎?很明顯答案是不會,因為深拷貝就相當於克隆出了一個全新的個體,兩者不再有任何關係
>>> b[2][0]=5
>>> b
[1, 2, [5, 4]]
>>> a
[1, 2, [3, 4]]