【Python成長之路】快速理解複製、淺拷貝、深拷貝

 

哈嘍大家好,我是鵬哥。

今天想聊聊的主題是 —— 淺拷貝/深拷貝

 

~~~上課鈴~~~

 

赤伶HITA - 赤伶

1

寫在前面

淺拷貝、深拷貝的知識點,在python筆試和麪試的過程中,經常被作爲考題來考察應聘者的基本功。比如問你“請簡單闡述下淺拷貝、深拷貝之間的差異”,或者給你一個變量分別進行復制、淺拷貝、深拷貝來求新值。

因此,我就簡單把自己的理解記錄下,希望對部分初學者有所幫助。

2

示例代碼

在進行示例代碼展示前,我們先理解下什麼叫 複製、淺拷貝、深拷貝。

【直接賦值】:其實就是對象的引用(別名)。 

【淺拷貝(copy)】:拷貝父對象,不會拷貝對象的內部的子對象。 

【深拷貝(deepcopy)】:copy 模塊的 deepcopy 方法,完全拷貝了父對象及其子對象。

好吧,反正我開始瞭解這些時對這 麼官方的介紹是看不懂的,完全不知道是 什麼意思。

Talk is cheap, Show me the code!

a = 1# 複製b = a# 淺拷貝c = copy.copy(a)# 深拷貝d = copy.deepcopy(a)

在進行介紹複製、淺拷貝、深拷貝前,我們還要了解下可變對象(dict/list/set)、不可變對象(int/str/float/tuple)。

 

【知識點1】可變對象、不可變對象

【可變對象】:當有需要改變對象內部的值的時候,這個對象的id不發生變化。 

【不可變對象】:當有需要改變對象內部的值的時候,這個對象的id會發生變化。​​​​​​​

a = 1print(id(a), a)a = 2print(id(a), a)A = [1]print(id(A), A)A.append(2)print(id(A), A)A = [1,2]print(id(A),A)
結果:8791349191504 18791349191536 234759304 [1]34759304 [1, 2]34759368 [1, 2]

從上述代碼可以看到,對不可變對象a進行值改變時,a的地址也發生了變化;但對可變對象A進行值改變時,內存地址是不變的,仍然是34759304。(這裏注意,A=[1,2]是重新賦值,所以是新地址,可以理解成是A‘,已不再是A)

 

【知識點2】對不可變對象進行復制、淺/深拷貝

對不可變對象,複製/淺拷貝/深拷貝都是引用原對象的內存地址。

import copy
a = 1print(id(a), a)print(id(a), a)# 複製b = a# 淺拷貝c = copy.copy(a)# 深拷貝d = copy.deepcopy(a)a = 2print(id(a), a)print(id(b), b)print(id(c), c)print(id(d), d)
結果:8791347356496 1  -- 變化前的a8791347356528 2  -- 變化後的a8791347356496 1  -- 複製8791347356496 1  -- 淺拷貝8791347356496 1  -- 深拷貝

因此,對於不可變對象,如果原對象發生什麼變化,複製/淺拷貝/深拷貝都不會跟着變。就像對於一個又沒有上進心又窮的小夥子,無論什麼女生都不會跟着他過一生,這男的要怎麼樣就怎麼樣吧,沒人管他。

 

【知識點3】對可變對象進行復制、淺/深拷貝​​​​​​​

import copy
a = [1, 2, [3, 4]]print(id(a), a)# 複製b = a# 淺拷貝c = copy.copy(a)# 深拷貝d = copy.deepcopy(a)a[1] = 5a[2].append(6)a.append(7)print(id(a), a)print(id(b), b)print(id(c), c)print(id(d), d)
結果:43241288 [1, 2, [3, 4]]          -- 變化前的a43241288 [1, 5, [3, 4, 6], 7]    -- 變化後的a43241288 [1, 5, [3, 4, 6], 7]    -- 複製43311048 [1, 2, [3, 4, 6]]       -- 淺拷貝43310984 [1, 2, [3, 4]]          -- 深拷貝

複製:原對象怎麼變,我跟着變。另外,這裏在可以發現對於可變對象,a變化前後的內存地址是不變的;就像老人們常說的“嫁雞隨雞,嫁狗隨狗”。

 

淺拷貝:原對象的外層元素地址變化,內層元素的地址不變。因爲淺拷貝是會拷貝原對象的父對象,因此只有外層元素的內存地址發生了變化,因此通過a.append(7)改變外層元素,即外層的列表時,對淺拷貝是不影響的;

那這裏你會肯定會問,爲什麼a[1]=5不變,而a[2].append(6)又發生了變化?因爲a[1]是不可變對象,a[2]是可變對象。前面已經提過了,對於不可變對象是不會跟着變的。對於可變對象的子對象變了,淺拷貝是會跟着變的。

就像上面提到的又沒有上進心又窮的小夥子,如果只是改變外觀(外層元素地址變了)或者變得有錢(窮屬性是不可變對象),是不會挽回前女友(淺拷貝)的心,只有改變了上進心(上進心屬性是可變對象),前女友纔會回心轉意而發生變化。

當然如果a[2]是通過a[2]=[3,4,6]來改變的話,淺拷貝是不會跟着變化的。原理已經在第1個知識點裏講過了,這裏不再複述。你可以理解成小夥子完全換了顆心臟,都不再是原先那個心臟了。(好吧,這個比方有點牽強。)

 

深拷貝:原對象的外層/內層元素地址都變化。這個就好理解了,前女友對你徹底死心,無論你怎麼變都不要你了。

 

綜上所述,複製相當於是無論什麼條件都願意跟着你結婚的好女孩;淺拷貝相當於有機會回到你身邊的前女友,但要看你表現;深拷貝是完全對你死心的前女友。(和面試官可別這麼說哈!)

3

補充點

如果對複製/淺拷貝/深拷貝的對象進行改變,那原對象會怎麼變呢?​​​​​​​

import copy
a = [1, 2, [3, 4]]print(id(a), a)# 複製b = a# 淺拷貝c = copy.copy(a)# 深拷貝d = copy.deepcopy(a)b[0] = 9print(id(a), a)c[1] = 10c[2].append(11)print(id(a), a)d[2].append(12)print(id(a), a)
結果:43241416 [1, 2, [3, 4]]  -- 原對象a43241416 [9, 2, [3, 4]]   -- 對複製進行改變後的a43241416 [9, 2, [3, 4, 11]]  --對淺拷貝進行改變後的a43241416 [9, 2, [3, 4, 11]]  -- 對深拷貝進行改變後的a

我想大家應該能理解c[1] = 10爲什麼不改變原對象,而c[2].append(11)能改變原對象的原理吧 ?

4

總結

我覺得,理解複製/淺拷貝/深拷貝的關鍵還是要看對應外層元素/子層元素的地址有沒有變化。地址變了,那就完全是新的對象;如果地址不變,那地址所對應的值變化,也就跟着變化了。另外,還要看原對象是可變對象還是不可變對象。

 

~~~下課鈴~~~

 

【往期熱門文章】:

【Python成長之路】10行代碼教你免費觀看無廣告版的《慶餘年》騰訊視頻

【Python成長之路】如何用python開發自己的iphone應用程序,並添加至siri指令

【Python成長之路】從 零做網站開發 -- 基於Flask和JQuery,實現表格管理平臺

 

點擊下方詩句,可以留言互動喔  

四方食事,不過一碗人間煙火。萬千得失,不過一段燭光消逝。

 

【關注“鵬哥賊優秀”公衆號,回覆“python學習材料”,將會有python基礎學習、機器學習、數據挖掘、高級編程教程等100G視頻資料,及100+份python相關電子書免費贈送!】

 

掃描二維碼

    與鵬哥一起

學python吧!

​​​​​​​

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章