1. 程式人生 > >[Python3 填坑] 009 深拷貝與淺拷貝

[Python3 填坑] 009 深拷貝與淺拷貝

[1] 創建 官方文檔 包含 lan 賬號 引用 新的 官方

目錄

  • 1. print( 坑的信息 )
  • 2. 開始填坑
    • (1) 問題描述
    • (2) Python3.7.2 官方文檔
    • (3) 先說結論
      • 1) 復合對象談深拷貝與淺拷貝的區別才有意義
      • 2) 深拷貝常伴隨兩個問題,而淺拷貝對此無需擔心
      • 3) deepcopy() 通過以下方式避免 2) 的問題:
      • 4) 補充
    • (4) 少廢話,上例子
    • (5) 召喚深拷貝
    • (6) 關於不可變元素

1. print( 坑的信息 )

  • 挖坑時間:2019/01/10
  • 明細
坑的編碼 內容
Py006-3 Python3 中的深拷貝與淺拷貝



2. 開始填坑

(1) 問題描述

個人覺得深拷貝與淺拷貝裏面大有文章,此篇隨筆只能算是淺談。

(2) Python3.7.2 官方文檔

  • 文檔地址
  • 源碼地址

(3) 先說結論

  1. 描述“深拷貝與“淺拷貝”時,常與“賦值”進行對比
  2. 簡單地說
    • 賦值,就像給原對象取個昵稱,無論呼本名還是喚昵稱,說的都是同一個事物
    • 深拷貝,拷貝的是原對象內部的元素,拷貝後井水不犯河水,是一個真正的副本
    • 淺拷貝,拷貝的是原對象內部數據的地址,拷貝後藕斷絲連,並不是一個真正的副本
  3. 淺拷貝的補充
    • 拷貝後的新對象占用新的空間,但其內部的元素指向原對象內部對應元素的地址
    • 當原對象中的可變元素發生變化時,新對象中的對應元素同步變化
    • 當原對象中的不可變元素重新生成時,新對象中的對應元素保持不變
    • 只拷貝第一層元素,有人稱其為“頂層拷貝”
    • 在前期做一個淺拷貝可以防止後期因變量名眾多而混亂
    • 可應用於“聯合賬號”等

  • 對 copy 模塊而言

1) 復合對象談深拷貝與淺拷貝的區別才有意義

  • 復合對象:包含其他對象的對象,如列表、類實例等

  • 深拷貝會構造一個新的復合對象,然後遞歸地將在原始對象中所有元素的副本對應地寫入新復合對象中
  • 淺拷貝會構造一個新的復合對象,然後(在允許的前提下)向其中寫入對原始對象的引用

2) 深拷貝常伴隨兩個問題,而淺拷貝對此無需擔心

  • 遞歸對象(直接或間接包含對自身引用的復合對象)可能導致遞歸循環
  • 因為深拷貝會復制原對象的一切,所以可能復制過多的內容,例如打算在副本之間共享的數據

3) deepcopy() 通過以下方式避免 2) 的問題:

  • 保存在當前復制過程中已經復制的對象的 memo 字典
  • 讓用戶定義的類重寫復制操作或復制的組件集

4) 補充

  • 它不能拷貝模塊、方法、堆棧跟蹤、堆棧幀、文件、套接字、窗口、數組等
  • 可以重寫 copy.copy(x) 和 copy.deepcopy(x[, memo])

(4) 少廢話,上例子

對於可變或包含可變項的容器,有時需要一個可以在不破壞其它容器的情況下更改自身的副本。

# 例1 從對象的地址上看

list1_1 = [1, 2, 3, 4, 5]
list1_2 = list1_1           # Python 中的賦值語句不復制對象,它們會在目標和對象之間創建綁定關系
list1_3 = list1_1[:]        # 通過分配整個列表的切片來制作列表的淺拷貝
list1_4 = list1_1.copy()    # 使用 list.copy() 來制作列表的淺拷貝
                            # 同理,可以使用 dict.copy() 來制作字典的淺拷貝

print("list1_1 =", list1_1)
print("list1_2 =", list1_2)
print("list1_3 =", list1_3)
print("list1_4 =", list1_4)
print(‘-‘*30)

print("id(list1_1) =", id(list1_1))
print("id(list1_2) =", id(list1_2))
print("id(list1_3) =", id(list1_3))
print("id(list1_4) =", id(list1_4))
  • 運行結果

list1_1 = [1, 2, 3, 4, 5]
list1_2 = [1, 2, 3, 4, 5]
list1_3 = [1, 2, 3, 4, 5]
list1_4 = [1, 2, 3, 4, 5]
------------------------------
id(list1_1) = 1790322044808
id(list1_2) = 1790322044808
id(list1_3) = 1790321996744
id(list1_4) = 1790321982024


# 例2 從對象的元素上看(我可能有些啰嗦了)

list2_1 = [1, 2, 3, 4, 5]
list2_2 = list2_1           # 賦值
list2_3 = list2_1[:]        # 淺拷貝
list2_4 = list2_1.copy()    # 淺拷貝

print("list2_1 =", list2_1)
print("list2_2 =", list2_2)
print("list2_3 =", list2_3)
print("list2_4 =", list2_4)
print(‘-‘*30)

list2_1[0] = 661
print("* list2_1 =", list2_1)
print("* list2_2 =", list2_2)
print("* list2_3 =", list2_3)
print("* list2_4 =", list2_4)
print(‘-‘*30)

list2_2[1] = 662
print("* list2_1‘ =", list2_1)
print("* list2_2‘ =", list2_2)
print("* list2_3‘ =", list2_3)
print("* list2_4‘ =", list2_4)
print(‘-‘*30)

list2_3[2] = 663
print("* list2_1‘‘ =", list2_1)
print("* list2_2‘‘ =", list2_2)
print("* list2_3‘‘ =", list2_3)
print("* list2_4‘‘ =", list2_4)
print(‘-‘*30)

list2_4[3] = 664
print("* list2_1‘‘‘ =", list2_1)
print("* list2_2‘‘‘ =", list2_2)
print("* list2_3‘‘‘ =", list2_3)
print("* list2_4‘‘‘ =", list2_4)
  • 運行結果

list2_1 = [1, 2, 3, 4, 5]
list2_2 = [1, 2, 3, 4, 5]
list2_3 = [1, 2, 3, 4, 5]
list2_4 = [1, 2, 3, 4, 5]
------------------------------
* list2_1 = [661, 2, 3, 4, 5]
* list2_2 = [661, 2, 3, 4, 5]
* list2_3 = [1, 2, 3, 4, 5]
* list2_4 = [1, 2, 3, 4, 5]
------------------------------
* list2_1‘ = [661, 662, 3, 4, 5]
* list2_2‘ = [661, 662, 3, 4, 5]
* list2_3‘ = [1, 2, 3, 4, 5]
* list2_4‘ = [1, 2, 3, 4, 5]
------------------------------
* list2_1‘‘ = [661, 662, 3, 4, 5]
* list2_2‘‘ = [661, 662, 3, 4, 5]
* list2_3‘‘ = [1, 2, 663, 4, 5]
* list2_4‘‘ = [1, 2, 3, 4, 5]
------------------------------
* list2_1‘‘‘ = [661, 662, 3, 4, 5]
* list2_2‘‘‘ = [661, 662, 3, 4, 5]
* list2_3‘‘‘ = [1, 2, 663, 4, 5]
* list2_4‘‘‘ = [1, 2, 3, 664, 5]


# 例3 從元素的地址上看

list3_1 = [0, 1, 2, [3, 4, 5]]
list3_2 = list3_1               # 賦值
list3_3 = list3_1[:]            # 淺拷貝
list3_4 = list3_1.copy()        # 淺拷貝

print("list3_1 =", list3_1)
print("list3_2 =", list3_2)
print("list3_3 =", list3_3)
print("list3_4 =", list3_4)
print(‘-‘*30)

print("id(list3_1[0]) =", id(list3_1[0]))
print("id(list3_2[0]) =", id(list3_2[0]))
print("id(list3_3[0]) =", id(list3_3[0]))
print("id(list3_4[0]) =", id(list3_4[0]))
print(‘-‘*30)

print("id(list3_1[3]) =", id(list3_1[3]))
print("id(list3_2[3]) =", id(list3_2[3]))
print("id(list3_3[3]) =", id(list3_3[3]))
print("id(list3_4[3]) =", id(list3_4[3]))
print(‘-‘*30)

print("id(list3_1[3][0]) =", id(list3_1[3][0]))
print("id(list3_2[3][0]) =", id(list3_2[3][0]))
print("id(list3_3[3][0]) =", id(list3_3[3][0]))
print("id(list3_4[3][0]) =", id(list3_4[3][0]))
  • 運行結果

list3_1 = [0, 1, 2, [3, 4, 5]]
list3_2 = [0, 1, 2, [3, 4, 5]]
list3_3 = [0, 1, 2, [3, 4, 5]]
list3_4 = [0, 1, 2, [3, 4, 5]]
------------------------------
id(list3_1[0]) = 140720367595232
id(list3_2[0]) = 140720367595232
id(list3_3[0]) = 140720367595232
id(list3_4[0]) = 140720367595232
------------------------------
id(list3_1[3]) = 2859246390984
id(list3_2[3]) = 2859246390984
id(list3_3[3]) = 2859246390984
id(list3_4[3]) = 2859246390984
------------------------------
id(list3_1[3][0]) = 140720367595328
id(list3_2[3][0]) = 140720367595328
id(list3_3[3][0]) = 140720367595328
id(list3_4[3][0]) = 140720367595328


# 例4.1 關於層數(第 2 層)

list4_1 = [0, 1, 2, [3, 4, 5]]
list4_2 = list4_1
list4_3 = list4_1[:]
list4_4 = list4_1.copy()

print("list4_1 =", list4_1)
print("list4_2 =", list4_2)
print("list4_3 =", list4_3)
print("list4_4 =", list4_4)
print(‘-‘*30)

list4_1[3].append(6)
print("* list4_1 =", list4_1)
print("* list4_2 =", list4_2)
print("* list4_3 =", list4_3)
print("* list4_4 =", list4_4)
print(‘-‘*30)

list4_2[3].append(7)
print("* list4_1‘ =", list4_1)
print("* list4_2‘ =", list4_2)
print("* list4_3‘ =", list4_3)
print("* list4_4‘ =", list4_4)
print(‘-‘*30)

list4_3[3][0] = 666
print("* list4_1‘‘ =", list4_1)
print("* list4_2‘‘ =", list4_2)
print("* list4_3‘‘ =", list4_3)
print("* list4_4‘‘ =", list4_4)
print(‘-‘*30)

list4_4[3][1] = 888
print("* list4_1‘‘‘ =", list4_1)
print("* list4_2‘‘‘ =", list4_2)
print("* list4_3‘‘‘ =", list4_3)
print("* list4_4‘‘‘ =", list4_4)
  • 運行結果

list4_1 = [0, 1, 2, [3, 4, 5]]
list4_2 = [0, 1, 2, [3, 4, 5]]
list4_3 = [0, 1, 2, [3, 4, 5]]
list4_4 = [0, 1, 2, [3, 4, 5]]
------------------------------
* list4_1 = [0, 1, 2, [3, 4, 5, 6]]
* list4_2 = [0, 1, 2, [3, 4, 5, 6]]
* list4_3 = [0, 1, 2, [3, 4, 5, 6]]
* list4_4 = [0, 1, 2, [3, 4, 5, 6]]
------------------------------
* list4_1‘ = [0, 1, 2, [3, 4, 5, 6, 7]]
* list4_2‘ = [0, 1, 2, [3, 4, 5, 6, 7]]
* list4_3‘ = [0, 1, 2, [3, 4, 5, 6, 7]]
* list4_4‘ = [0, 1, 2, [3, 4, 5, 6, 7]]
------------------------------
* list4_1‘‘ = [0, 1, 2, [666, 4, 5, 6, 7]]
* list4_2‘‘ = [0, 1, 2, [666, 4, 5, 6, 7]]
* list4_3‘‘ = [0, 1, 2, [666, 4, 5, 6, 7]]
* list4_4‘‘ = [0, 1, 2, [666, 4, 5, 6, 7]]
------------------------------
* list4_1‘‘‘ = [0, 1, 2, [666, 888, 5, 6, 7]]
* list4_2‘‘‘ = [0, 1, 2, [666, 888, 5, 6, 7]]
* list4_3‘‘‘ = [0, 1, 2, [666, 888, 5, 6, 7]]
* list4_4‘‘‘ = [0, 1, 2, [666, 888, 5, 6, 7]]

從例3 的 id 也不難想象這個結果。


# 例4.2 關於層數(第 1 層;我啰嗦了,此例同例2)

list4_1 = [0, 1, 2, [3, 4, 5]]
list4_2 = list4_1
list4_3 = list4_1[:]
list4_4 = list4_1.copy()

print("list4_1 =", list4_1)
print("list4_2 =", list4_2)
print("list4_3 =", list4_3)
print("list4_4 =", list4_4)
print(‘-‘*50)

list4_1.append(6)
print("* list4_1 =", list4_1)
print("* list4_2 =", list4_2)
print("* list4_3 =", list4_3)
print("* list4_4 =", list4_4)
print(‘-‘*50)

list4_2.extend([7, 8])
print("* list4_1‘ =", list4_1)
print("* list4_2‘ =", list4_2)
print("* list4_3‘ =", list4_3)
print("* list4_4‘ =", list4_4)
print(‘-‘*50)

list4_3.insert(3, "YorkFish")
print("* list4_1‘‘ =", list4_1)
print("* list4_2‘‘ =", list4_2)
print("* list4_3‘‘ =", list4_3)
print("* list4_4‘‘ =", list4_4)
print(‘-‘*50)

list4_4.insert(0, "YorkFish")
print("* list4_1‘‘‘ =", list4_1)
print("* list4_2‘‘‘ =", list4_2)
print("* list4_3‘‘‘ =", list4_3)
print("* list4_4‘‘‘ =", list4_4)
  • 運行結果

list4_1 = [0, 1, 2, [3, 4, 5]]
list4_2 = [0, 1, 2, [3, 4, 5]]
list4_3 = [0, 1, 2, [3, 4, 5]]
list4_4 = [0, 1, 2, [3, 4, 5]]
--------------------------------------------------
* list4_1 = [0, 1, 2, [3, 4, 5], 6]
* list4_2 = [0, 1, 2, [3, 4, 5], 6]
* list4_3 = [0, 1, 2, [3, 4, 5]]
* list4_4 = [0, 1, 2, [3, 4, 5]]
--------------------------------------------------
* list4_1‘ = [0, 1, 2, [3, 4, 5], 6, 7, 8]
* list4_2‘ = [0, 1, 2, [3, 4, 5], 6, 7, 8]
* list4_3‘ = [0, 1, 2, [3, 4, 5]]
* list4_4‘ = [0, 1, 2, [3, 4, 5]]
--------------------------------------------------
* list4_1‘‘ = [0, 1, 2, [3, 4, 5], 6, 7, 8]
* list4_2‘‘ = [0, 1, 2, [3, 4, 5], 6, 7, 8]
* list4_3‘‘ = [0, 1, 2, ‘YorkFish‘, [3, 4, 5]]
* list4_4‘‘ = [0, 1, 2, [3, 4, 5]]
--------------------------------------------------
* list4_1‘‘‘ = [0, 1, 2, [3, 4, 5], 6, 7, 8]
* list4_2‘‘‘ = [0, 1, 2, [3, 4, 5], 6, 7, 8]
* list4_3‘‘‘ = [0, 1, 2, ‘YorkFish‘, [3, 4, 5]]
* list4_4‘‘‘ = [‘YorkFish‘, 0, 1, 2, [3, 4, 5]]


(5) 召喚深拷貝

# 例5 賦值與淺拷貝、深拷貝的不同

import copy     # 導入 copy 模塊

list5_1 = [0, 1, 2, [‘a‘, ‘b‘]]
list5_2 = list5_1                   # 賦值
list5_3 = list5_1.copy()            # 淺拷貝;用 list5_1[:] 效果一樣
list5_4 = copy.copy(list5_1)        # 淺拷貝
list5_5 = copy.deepcopy(list5_1)    # 遲來的深拷貝

print("list5_1 =", list5_1)
print("list5_2 =", list5_2)
print("list5_3 =", list5_3)
print("list5_4 =", list5_4)
print("list5_5 =", list5_5)
print(‘-‘*30)

list5_1.append(4)
list5_1[3].append(‘c‘)
print("list5_1‘ =", list5_1)
print("list5_2‘ =", list5_2)
print("list5_3‘ =", list5_3)
print("list5_4‘ =", list5_4)
print("list5_5‘ =", list5_5)
  • 運行結果

list5_1 = [0, 1, 2, [‘a‘, ‘b‘]]
list5_2 = [0, 1, 2, [‘a‘, ‘b‘]]
list5_3 = [0, 1, 2, [‘a‘, ‘b‘]]
list5_4 = [0, 1, 2, [‘a‘, ‘b‘]]
list5_5 = [0, 1, 2, [‘a‘, ‘b‘]]
------------------------------
list5_1‘ = [0, 1, 2, [‘a‘, ‘b‘, ‘c‘], 4]
list5_2‘ = [0, 1, 2, [‘a‘, ‘b‘, ‘c‘], 4]
list5_3‘ = [0, 1, 2, [‘a‘, ‘b‘, ‘c‘]]
list5_4‘ = [0, 1, 2, [‘a‘, ‘b‘, ‘c‘]]
list5_5‘ = [0, 1, 2, [‘a‘, ‘b‘]]

深拷貝的第二層沒有像淺拷貝一樣隨原對象的變化而變化。


(6) 關於不可變元素

# 例6 “不動如山”的不可變元素

import copy

list6_1 = [0, 1, 2, (3, 4, 5)]
list6_2 = list6_1
list6_3 = list6_1.copy()        # 用 list6_1[:] 效果一樣
list6_4 = copy.copy(list6_1)
list6_5 = copy.deepcopy(list6_1)

print("list6_1 =", list6_1)
print("list6_2 =", list6_2)
print("list6_3 =", list6_3)
print("list6_4 =", list6_4)
print("list6_5 =", list6_5)
print(‘-‘*30)

print("id(ist6_1[3]) =", id(list6_1[3]))
print("id(ist6_2[3]) =", id(list6_2[3]))
print("id(ist6_3[3]) =", id(list6_3[3]))
print("id(ist6_4[3]) =", id(list6_4[3]))
print("id(ist6_5[3]) =", id(list6_5[3]))
print(‘-‘*30)

list6_1[3] = (6, 6, 6)          # list6_1 與 list6_2 是一體的
print("list6_1‘ =", list6_1)
print("list6_2‘ =", list6_2)
print("list6_3‘ =", list6_3)
print("list6_4‘ =", list6_4)
print("list6_5‘ =", list6_5)
print(‘-‘*30)

print("id(ist6_1[3]‘) =", id(list6_1[3]))
print("id(ist6_2[3]‘) =", id(list6_2[3]))
print("id(ist6_3[3]‘) =", id(list6_3[3]))
print("id(ist6_4[3]‘) =", id(list6_4[3]))
print("id(ist6_5[3]‘) =", id(list6_5[3]))
print(‘-‘*30)

list6_2[3] = (8, 8, 8)
list6_3[3] = (8, 8, 8)
list6_4[3] = (8, 8, 8)
list6_5[3] = (8, 8, 8)
print("list6_1‘‘ =", list6_1)
print("list6_2‘‘ =", list6_2)
print("list6_3‘‘ =", list6_3)
print("list6_4‘‘ =", list6_4)
print("list6_5‘‘ =", list6_5)
print(‘-‘*30)

print("id(ist6_1[3]‘‘) =", id(list6_1[3]))
print("id(ist6_2[3]‘‘) =", id(list6_2[3]))
print("id(ist6_3[3]‘‘) =", id(list6_3[3]))  # 註意 id
print("id(ist6_4[3]‘‘) =", id(list6_4[3]))  # 註意 id
print("id(ist6_5[3]‘‘) =", id(list6_5[3]))  # 註意 id
  • 運行結果

list6_1 = [0, 1, 2, (3, 4, 5)]
list6_2 = [0, 1, 2, (3, 4, 5)]
list6_3 = [0, 1, 2, (3, 4, 5)]
list6_4 = [0, 1, 2, (3, 4, 5)]
list6_5 = [0, 1, 2, (3, 4, 5)]
------------------------------
id(ist6_1[3]) = 2683399214208
id(ist6_2[3]) = 2683399214208
id(ist6_3[3]) = 2683399214208
id(ist6_4[3]) = 2683399214208
------------------------------
list6_1‘ = [0, 1, 2, (6, 6, 6)]
list6_2‘ = [0, 1, 2, (6, 6, 6)]
list6_3‘ = [0, 1, 2, (3, 4, 5)]
list6_4‘ = [0, 1, 2, (3, 4, 5)]
list6_5‘ = [0, 1, 2, (3, 4, 5)]
------------------------------
id(ist6_1[3]‘) = 2683399759624
id(ist6_2[3]‘) = 2683399759624
id(ist6_3[3]‘) = 2683399214208
id(ist6_4[3]‘) = 2683399214208
id(ist6_5[3]‘) = 2683399214208
------------------------------
list6_1‘‘ = [0, 1, 2, (8, 8, 8)]
list6_2‘‘ = [0, 1, 2, (8, 8, 8)]
list6_3‘‘ = [0, 1, 2, (8, 8, 8)]
list6_4‘‘ = [0, 1, 2, (8, 8, 8)]
list6_5‘‘ = [0, 1, 2, (8, 8, 8)]
------------------------------
id(ist6_1[3]‘‘) = 2683399916856
id(ist6_2[3]‘‘) = 2683399916856
id(ist6_3[3]‘‘) = 2683399968520
id(ist6_4[3]‘‘) = 2683399759624
id(ist6_5[3]‘‘) = 2683399921880

Python3 能“偷懶”時必“偷懶”,對於可變元素,有變化才分配新地址。


在下學識有限,不足之處,還請諸位多多指教!

[Python3 填坑] 009 深拷貝與淺拷貝