Python中的深淺複製

引子

最近遇到了一個問題,在python中對於對象的複製有兩種,copy()以及deepcopy(),爲了研究他們之間的區別,寫下以下博客。
首先查看python的官方文檔,獲得以下解釋:python

淺層複製和深層複製之間的區別僅與複合對象 (即包含其餘對象的對象,如列表或類的實例) 相關:
一個 淺層複製 會構造一個新的複合對象,而後(在可能的範圍內)將原對象中找到的 引用 插入其中。
一個 深層複製 會構造一個新的複合對象,而後遞歸地將原始對象中所找到的對象的 副本 插入。

爲了明白其中的原理,作了以下實驗:code

簡單對象的複製

簡單對象是指不包含子對象的對象,也就是隻包含普通元素(數字,字符串)的對象,對於簡單對象,copy與deepcopy方法沒有什麼區別,下面的一段代碼,能夠先看一下效果是怎樣的。對象

import copy

if __name__ == '__main__':
    a = [1, 2, 3, 4]
    b = copy.copy(a)
    c = copy.deepcopy(a)  
    print(a == b)
    print(a is b)
    print(a == c)
    print(a is c)

執行上面的代碼,咱們能夠看到執行結果,深複製和淺複製的執行結果是同樣的遞歸

True # 說明 a 和 b 所指向的對象的內容相同
False # 說明 a 和 b 所指向的不是同一個對象(地址不一樣)
True # 說明 a 和 c 所指向的對象的內容相同
False # 說明 a 和 c 所指向的不是同一個對象(地址不一樣)

能夠用一張圖來解釋一下,爲何簡單對象的深淺複製是同樣的。內存

image-20210116191553273
咱們知道,上面的圖中,變量a指向一個List對象(或者說是一個List對象的引用),該對象在內存中佔用一個地址空間,當簡單對象執行copy和deepcopy中的對象時,咱們能夠看到不管時深複製仍是淺複製,都是在內存中新開闢一個地址空間,將原來對象中的內容複製過去,同時讓b成爲新對象的引用。所以,咱們看到a和b指向的對象是不一致的,可是內容是相同的。文檔

複雜對象的複製

複雜對象能夠理解爲另外包含其餘簡單對象的對象,也就是包含子對象的對象,例如:List中嵌套List,或者Dict中嵌套List等,對於複雜對象咱們先來看一個簡單的程序示例。字符串

import copy

if __name__ == '__main__':
    a = {'name': 'test', 'age': 56, 'address': [1, 2, 3, 4, 5]}
    b = copy.copy(a)
    print(a is b)
    print(a['address'] is b['address'])
    c = copy.deepcopy(a)
    print(a is c)
    print(a['address'] is c['address'])

執行結果以下:博客

False  # 說明 a 和 b 不是同一個對象的引用
True   # 說明 a中的address 和 b 中的 address 是同一個對象。黑人問號臉??
False  # 說明 a 和 c 不是同一個對象的引用
False  # 說明 a中的address 和 c 中的 address 不是同一個對象

下面我經過一張圖,來大概解釋一下爲何會出現上面的結果。PS:具體對象的對象不必定是按照圖中的方式,爲了可以說明原理,本圖中將子對象的存儲空間單獨抽出,方便理解。class

image-20210116194842905
咱們看到對於複雜對象(包含子對象的對象)的複製,深淺複製在實現原理上就有所不一樣了。經過上圖咱們能夠看到,複雜對象的深淺複製的區別在於複雜對象的子對象。能夠看到:test

總結

  • 對於簡單對象,不管是深複製仍是淺複製,咱們能夠看到,Python都是採用的直接在內存中開闢新的地址空間,而後將值複製到新的地址空間。
  • 對於複雜對象,複雜對象的簡單對象部分,按常規的簡單對象進行處理,對於複雜對象的子對象部分來講:深複製是在內存中開闢一個新的空間,而且將子對象複製到新的地址空間,可是對於淺複製而言,咱們能夠看到並無對子對象來開闢空間,經過圖看到,新複製的對象和原來的對象同時指向了同一個List對象(也就是同一個對象的引用),因此咱們看到a[‘address’]和b[‘address’]同時指向同一個對象。
相關文章
相關標籤/搜索