1. 程式人生 > >【Python】直接賦值,深拷貝和淺拷貝

【Python】直接賦值,深拷貝和淺拷貝

> 直接賦值: 物件的引用,也就是給物件起別名 > 淺拷貝: 拷貝父物件,但是不會拷貝物件的內部的子物件。 > 深拷貝: 拷貝父物件. 以及其內部的子物件 在之前的文章中,提到可變物件和不可變物件,接下來也是以這兩者的區別進行展開 # 直接賦值 **對於可變物件和不可變物件,將一個變數直接賦值給另外一個變數,兩者 id 值一致**,其實本質上是將變數量繫結到物件的過程. ```python >>> 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 ``` 關於修改新變數的值,對原有變數會產生的影響,在[可變物件和不可變物件](https://app.yinxiang.com/shard/s50/nl/10233243/52645b99-732f-4f0a-95be-206bb9b79033) 中也做了講述,這裡通過幾個例子,重新溫習一下 ## 不可變物件 ```python >>> 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` 進行的操作,其實是重新建立一個物件並繫結的結果: ![](https://img2020.cnblogs.com/blog/627405/202007/627405-20200709085853273-2105862698.gif) ## 可變物件 ```python >>> 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` 值保持不變 ![](https://img2020.cnblogs.com/blog/627405/202007/627405-20200709085944230-1206472986.gif) 從上圖不難看出,這個時候的 `id(n[0])` 的值,和未修改前的 `id`值應該不一樣,可以輸出看一下 ```python >>>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. 從文件中不難看出,上面提到深拷貝和淺拷貝兩者區別在於在複合物件,那接下來也只討論複合物件. ## 淺拷貝 注意到官方文件也提到對淺拷貝和深拷貝的定義,從上文中不難看出,**淺拷貝構建一個複合物件,然後將原有複合物件包含的物件插入到新的複合物件中** ![](https://img2020.cnblogs.com/blog/627405/202007/627405-20200709085958446-1136047155.png) 從上圖不難看出,**淺拷貝後,新複合物件包含的物件(可變或者不可變)的 id 值和原有物件包含的物件的 id 值相同** 看一下具體例子: ```python >>> 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]` 的由於沒有修改,則保持不變,真的是這樣嗎?讓我們驗證一下 ```python >>> b[0]=5 >>> b [5, 2, [3, 4]] >>> a [1, 2, [3, 4]] ``` 接下來我們要嘗試修改一下 `b[2]`,由於 `b[2]` 繫結的物件是 `list`,屬於可變物件,按照上面說的可變物件修改的規則,則修改後的 `b[2]` 的 `id` 值保持不變,但是其 `value` 值會發生改變. 同樣的讓我們通過例子驗證一下 ```python >>> 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]` 上 ## 深拷貝 **深拷貝構建一個複合物件,然後遞迴的將原有複合包含的物件的副本插入到新的複合物件中** ![](https://img2020.cnblogs.com/blog/627405/202007/627405-20200709090049610-1454549813.png) 若上圖所示,**深拷貝後,新的複合物件包含的物件,若物件為不可變物件,則 id 值保持不變,若物件為可變物件,則 id 值發生改變** 看一個例子: ```python >>> 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]` 產生影響嗎?很明顯答案是不會,因為深拷貝就相當於克隆出了一個全新的個體,兩者不再有任何關係 ```python >>> b[2][0]=5 >>> b [1, 2, [5, 4]] >>> a [1, 2, [3,