程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python之深淺拷貝

編輯:Python

Python之深淺拷貝

文章目錄

  • Python之深淺拷貝
      • 深拷貝和淺拷貝總結:
      • 淺拷貝案例
        • 對 可變數據類型 進行淺拷貝的進階篇
      • 深拷貝案例
      • 其他方式的拷貝
        • 分片式拷貝
        • 字典自帶的拷貝

學習深淺拷貝前,我們先了解一下Python中6大標准數據類型,其中有3個不可變數據類型,3個可變數據類型。 標准數據類型詳解

  • 不可變數據類型:

    • Number(整型)
    • String (字符串)
    • Tuple (元組)
  • 可變數據類型:

    • List(列表)
    • Dictionary(字典)
    • Set(集合)

深拷貝和淺拷貝總結:

淺拷貝:(copy)

  1. 對於不可變類型 Number 、String 、Tuple,淺拷貝僅僅是地址的指向,並不會開辟新的地址空間。
  2. 對於可變數據類型 List 、Dictionary 、Set,淺拷貝會開辟新的地址空間(僅僅是最頂層開辟新的地址空間,裡面的元素地址還是一樣的),進行淺拷貝。
  3. 淺拷貝後,改變原始對象中可變數據類型裡面元素的值,會同時影響拷貝對象的元素的值;改變原始對象中不可變數據類型裡面元素的值,只有原始對象裡面的值會改變。(操作拷貝對象對原始對象的也是同理)

深拷貝(deepcopy)

  1. 在深拷貝中,對於不可變數據類型 Number 、String 、Tuple,深拷貝仍然是地址的指向,並不會開辟新的地址空間。
  2. 對於可變數據類型 List 、Dictionary 、Set,深拷貝會開辟新的地址空間(最頂層地址和裡面的元素地址都會開辟新的地址空間),進行深拷貝。
  3. 深拷貝後,改變原始對象中的值(不區分可變類型和不可變類型),只有原始對象中的值受影響,拷貝對象不會被影響;同理,改變拷貝對象中的值(不區分可變類型和不可變類型),只有拷貝對象中的值受影響,原始對象不會被影響。

由於下面的案例比較長,主要是為了驗證上面的總結,個人建議,結合著上面的總結,把下面的案例自己動手操作一遍,你可以深淺拷貝有更加深刻的了解。

淺拷貝案例

import copy
# 不可變數據類型 Number String Tuple
# 對於不可變數據類型,淺拷貝僅僅是地址的指向,不會開辟新的地址空間
# 對於不可變類型 Number 操作 
num1 = 17
num2 = copy.copy(num1)
print("num1: " + str(num1) + ' , id :' + str(id(num1))) # num1: 17 , id :1346137664
print("num2: " + str(num2) + ' , id :' + str(id(num2))) # num1: 17 , id :1346137664
# num1 和 num2 的地址都相同
# 改變原始數據num1 只有 num1 受影響,拷貝對象不受影響
num1 = 55
print("num1: " + str(num1) + ' , id :' + str(id(num1))) # num1: 55 , id :1346138880
print("num2: " + str(num2) + ' , id :' + str(id(num2))) # num2: 17 , id :1346137664
# 對於不可變類型 String 操作 start
str1 = "hello"
str2 = copy.copy(str1)
print("str1: " + str(id(str1))) # str1: 2253301326824
print("str2: " + str(id(str2))) # str2: 2253301326824
# str1 和 str2 的地址都相同
# 對於不可變類型 Tuple 操作 start
tup1 = (18,'tom')
tup2 = copy.copy(tup1)
print("tup1: " + str(id(tup1))) # tup1: 2587894715912
print("tup2: " + str(id(tup2))) # tup2: 2587894715912
# tup1 和 tup2 的地址都相同
# 可變數據類型 List、Dictionary、Set
# 對於可變數據類型,淺拷貝會開辟新的空間地址(僅僅是最頂層開辟了新的空間)
# 對於可變類型 List 的操作 start
list1 = [33,44]
list2 = copy.copy(list1) # list2列表 淺拷貝 list1列表
print("list1中是: " + str(list1) + " , 地址是" + str(id(list1)))
print("list2中是: " + str(list1) + " , 地址是" + str(id(list2)))
# list1中是: [33, 44] , 地址是2301868933192
# list2中是: [33, 44] , 地址是2301868661192
# list1 和 list2 的地址不相同
print("list1[0]中字符串是: "+ str(list1[0]) + " , 地址是" + str(id(list1[0])))
print("list2[0]中字符串是: "+ str(list2[0]) + " , 地址是" + str(id(list2[0])))
# list1[0]中字符串是: 33 , 地址是1346138176
# list2[0]中字符串是: 33 , 地址是1346138176
# list1[0] 和 list2[0] 的地址相同
# 對於可變類型 Dictionary 的操作 start
dic1 = {
"one":"33","two":"44"}
dic2 = copy.copy(dic1)
print("dic1中內容是: " + str(dic1) + " , 地址是: " + str(id(dic1)))
print("dic2中內容是: " + str(dic2) + " , 地址是: " + str(id(dic2)))
# dic1中內容是: {'one': '33', 'two': '44'} , 地址是: 2332667211712
# dic2中內容是: {'one': '33', 'two': '44'} , 地址是: 2332667215944
# dic1 和 dic2 的地址不同
print('dic1["one"]中內容是: ' + str(dic1["one"]) + " , 地址是: " + str(id(dic1["one"])))
print('dic2["one"]中內容是: ' + str(dic2["one"]) + " , 地址是: " + str(id(dic2["one"])))
# dic1["one"]中內容是: 33 , 地址是: 2747176712768
# dic2["one"]中內容是: 33 , 地址是: 2747176712768
# dic1["one"] 和 dic2["one"]的地址相同
# 對於可變類型 Set 的操作 start
set1 = {
"ww","11"}
set2 = copy.copy(set1)
print("set1中內容是: " + str(set1) + " , 地址是: " + str(id(set1)))
print("set2中內容是: " + str(set2) + " , 地址是: " + str(id(set2)))
# set1中內容是: {'ww', '11'} , 地址是: 1787411341832
# set2中內容是: {'ww', '11'} , 地址是: 1787411342504
# set1 和 set2 的地址不同

對 可變數據類型 進行淺拷貝的進階篇

由於原理都是一樣的,下面的案例便以列表為例

# 對 List(列表) 進行淺拷貝的進階篇 : 當修改列表中 '可變類型' 和 '不可變類型’ 的值後的影響
# 淺拷貝,創建出一個新對象,並把舊對象中的元素的 引用地址 拷貝到新對象中的元素當中;
# 簡單的說,就是兩個 對象中的元素 通過淺拷貝指向的還是同一個地址
import copy
list1 = [1,2]
list2 = [3,4]
num = 55
list_sum1 = [list1,list2,num] # 把 list1 、list2 、num 合並到列表 list_sum1中
list_sum2 = copy.copy(list_sum1) # 把 列表list_sum1 中的元素淺拷貝到 列表list_sum2 中
print(list_sum1) # list_sum1 中的值:[[1, 2],[3, 4],55]
print(list_sum2) # list_sum2 中的值:[[1, 2],[3, 4],55]
print("num上: " + str(num) + " , num上的id: "+ str(id(num)))
num = 88
print("num下: "+ str(num) +" , num下的id: "+ str(id(num)))
# num上: 55 , num上的id: 1346138880
# num下: 88 , num下的id: 1346139936
# 上面的num 的地址 和 下面的num 的地址不一樣
print("把 88 復制給 num 後 , list_sum1中元素值: " + str(list_sum1))
print("把 88 復制給 num 後 , list_sum2中元素值: " + str(list_sum2))
# 把 88 復制給 num 後 , list_sum1中元素值: [[1, 2], [3, 4], 55]
# 把 88 復制給 num 後 , list_sum2中元素值: [[1, 2], [3, 4], 55]
# 把 88 復制給num 會得到一個全新的地址,這時 list_sum1 和 list_sum2 中的值不會發生改變
# 因為 這個num 和 上面其他的num 已經沒有關系了
list1[0] = 10
# 修改列表 list1 索引為0 的值,會使 list_sum1 和 list_sum2 中 索引為0 的元素的值都會發生改變,
# 因為列表 list1 是可變對象。
print("修改 list1[0] 的值等於 10 後 , list_sum1中元素值: " + str(list_sum1))
print("修改 list1[0] 的值等於 10 後 , list_sum2中元素值: " + str(list_sum2))
# 修改 list1[0] 的值等於 10 後 , list_sum1中元素值: [[10, 2], [3, 4], 55]
# 修改 list1[0] 的值等於 10 後 , list_sum2中元素值: [[10, 2], [3, 4], 55]
list_sum1[0] = 12
print("修改 list1[0] 的值等於 10 後 , list_sum1中元素值: " + str(list_sum1))
print("修改 list1[0] 的值等於 10 後 , list_sum1中元素值: " + str(list_sum2))
修改 list1[0] 的值等於 10 後 , list_sum1中元素值: [12, [3, 4], 55]
修改 list1[0] 的值等於 10 後 , list_sum1中元素值: [[10, 2], [3, 4], 55]
import copy
list1 = [1,2]
list2 = [3,4]
num = 55
list_sum1 = [list1,list2,num] # 把 list1 、list2 、num 合並到列表 list_sum1中
list_sum2 = copy.copy(list_sum1) # 把 列表list_sum1 中的元素淺拷貝到 列表list_sum2 中
print("list_sum1 的值: " + str(list_sum1) + " , list_sum1的id: " + str(id(list_sum1)))
print("list_sum2 的值: " + str(list_sum2) + " , list_sum2的id: " + str(id(list_sum2)))
# list_sum1 的值: [[1, 2], [3, 4], 55] , list_sum1的id: 2750138189000
# list_sum2 的值: [[1, 2], [3, 4], 55] , list_sum2的id: 2750138188936
print("list_sum1[0] 的值: " + str(list_sum1[0]) + " , list_sum1[0]的id: " + str(id(list_sum1[0])))
print("list_sum2[0] 的值: " + str(list_sum2[0]) + " , list_sum2[0]的id: " + str(id(list_sum2[0])))
# list_sum1[0] 的值: [1, 2] , list_sum1[0]的id: 2028849168584
# list_sum2[0] 的值: [1, 2] , list_sum2[0]的id: 2028849168584
print("list_sum1[0][0] 的值: " + str(list_sum1[0][0]) + " , list_sum1[0][0]的id: " + str(id(list_sum1[0][0])))
print("list_sum2[0][0] 的值: " + str(list_sum2[0][0]) + " , list_sum2[0][0]的id: " + str(id(list_sum2[0][0])))
# list_sum1[0][0] 的值: 1 , list_sum1[0][0]的id: 1346137152
# list_sum2[0][0] 的值: 1 , list_sum2[0][0]的id: 1346137152
print("list_sum1[1] 的值: " + str(list_sum1[1]) + " , list_sum1[1]的id: " + str(id(list_sum1[1])))
print("list_sum2[1] 的值: " + str(list_sum2[1]) + " , list_sum2[1]的id: " + str(id(list_sum2[1])))
# list_sum1[1] 的值: [3, 4] , list_sum1[1]的id: 2589457930824
# list_sum2[1] 的值: [3, 4] , list_sum2[1]的id: 2589457930824
# 由上述例子可以看出:
# 改動 list_sum1 中的 可變類型 , 會影響 list_sum2 ; 改動 list_sum1中的可變類型,同理會影響 list_sum1
# 改動 list_sum2 中的 不可變類型 , 只影響 lsit_sum2 自己,不會影響 list_sum1

關於 = 符號,可以參考python基礎(5):深入理解 python 中的賦值、引用、拷貝、作用域

深拷貝案例

由於篇幅有限,深拷貝的案例就不在列舉了,有興趣的可以自己按照淺拷貝的案例方式自己動手實踐一下,後面字典自帶的拷貝可以實現深拷貝,可以結合下面的案例。

  1. 淺拷貝,除了父層拷貝,對子元素也要進行拷貝(本質上遞歸淺拷貝)
  2. 經過深拷貝後,原對象和拷貝的新對象所有的元素地址都是不一樣的
  3. 可以用分片表達式進行深拷貝
  4. 字典自帶的copy方法可以實現深拷貝
# 在深拷貝中,對於不可變類型 Number String Tuple,深拷貝仍然是地址指向,不會開辟新空間拷貝值
# 對於可變類型 List Dictionaty Set ,深拷貝會開辟新的空間地址,進行拷貝
# 淺拷貝,除了頂層拷貝,還對子元素進行了拷貝(本質上遞歸淺拷貝)
# 經過深拷貝後,原始對象和拷貝對象所有的元素地址都沒有相同的了
# 在之前的淺拷貝中,子元素是不會開辟新空間來拷貝的
# 但是在深拷貝中,子元素也進行拷貝

其他方式的拷貝

分片式拷貝

# 分片表達式拷貝 start
list1 = [1,2]
list2 = [3,4]
num = 55
list_sum = [list1,list2,num]
print(list_sum)
# [[1, 2], [3, 4], 55]
nlist = list_sum[:]
print(nlist)
# [[1, 2], [3, 4], 55]
print('list_sum 的值: ' + str(list_sum) + ', list_sum 的id: ' + str(id(list_sum)))
# list_sum 的值: [[1, 2], [3, 4], 55], list_sum 的id: 2042818430088
print('list_sum[0] 的值: ' + str(list_sum[0]) + ', list_sum[0] 的id: ' + str(id(list_sum[0])))
# list_sum[0] 的值: [1, 2], list_sum[0] 的id: 2042818429064
print('list_sum[1][0] 的值: ' + str(list_sum[1][0]) + ', list_sum[1][0] 的id: ' + str(id(list_sum[1][0])))
# list_sum[1][0] 的值: 3, list_sum[1][0] 的id: 1346137216
print('list_sum[2] 的值: ' + str(list_sum[2]) + ', list_sum[2] 的id: ' + str(id(list_sum[2])))
# list_sum[2] 的值: 55, list_sum[2] 的id: 1346138880
print('nlist 的值: ' + str(nlist) + ', nlist 的id: ' + str(id(nlist)))
# nlist 的值: [[1, 2], [3, 4], 55], nlist 的id: 2042818430024
print('nlist[0] 的值: ' + str(nlist[0]) + ', nlist[0] 的id: ' + str(id(nlist[0])))
# nlist[0] 的值: [1, 2], nlist[0] 的id: 2042818429064
print('nlist[1][0] 的值: ' + str(nlist[1][0]) + ', nlist[1][0] 的id: ' + str(id(nlist[1][0])))
# nlist[1][0] 的值: 3, nlist[1][0] 的id: 1346137216
print('nlist[2] 的值: ' + str(nlist[2]) + ', nlist[2] 的id: ' + str(id(nlist[2])))
# nlist[2] 的值: 55, nlist[2] 的id: 1346138880
# 分片式拷貝是淺拷貝

字典自帶的拷貝

**字典自帶的 copy 方法可以實現深拷貝 **

# 字典自帶的 copy 方法可以實現的拷貝 start
import copy
dic0 = {
'key1':[0,1,2],'key2':10}
dic1 = dic0 # 賦值
dic2 = dic0.copy() # 淺拷貝
dic3 = copy.deepcopy(dic0) # 深拷貝
print("原本值dic0的值: " + str(dic0) + ", 原本值dic0的id:" + str(id(dic0)))
# 原本值dic0的值: {'key1': [0, 1, 2], 'key2': 10}, 原本值dic0的id:2541879095232
print("賦值 dic1 的值: " + str(dic1) + ", 賦值 dic1 的id:" + str(id(dic1)))
# 賦值 dic1 的值: {'key1': [0, 1, 2], 'key2': 10}, 賦值 dic1 的id:2541879095232
print("淺拷貝dic2的值: " + str(dic2) + ", 淺拷貝dic2的id:" + str(id(dic2)))
# 淺拷貝dic2的值: {'key1': [0, 1, 2], 'key2': 10}, 淺拷貝dic2的id:2541879099464
print("深拷貝dic3的值: " + str(dic3) + ", 深拷貝dic2的id:" + str(id(dic3)))
# 深拷貝dic3的值: {'key1': [0, 1, 2], 'key2': 10}, 深拷貝dic2的id:2541879972488
#對於字典中key(鍵)的值是可變類型的情況
print("原本值dic0['key1']的值: " + str(dic0['key1']) + ", 原本值dic0['key1'] 的id:" + str(id(dic0['key1'])))
# 原本值dic0['key1']的值: [0, 1, 2], 原本值dic0['key1'] 的id:2541881647240
print("賦值 dic1['key1'] 的值: " + str(dic1['key1']) + ", 賦值 dic1['key1'] 的id:" + str(id(dic1['key1'])))
# 賦值 dic1['key1'] 的值: [0, 1, 2], 賦值 dic1['key1'] 的id:2541881647240
print("淺拷貝dic2['key1']的值: " + str(dic2['key1']) + ", 淺拷貝dic2['key1'] 的id:" + str(id(dic2['key1'])))
# 淺拷貝dic2['key1']的值: [0, 1, 2], 淺拷貝dic2['key1'] 的id:2541881647240
print("深拷貝dic3['key1']的值: " + str(dic3['key1']) + ", 深拷貝dic3['key1'] 的id:" + str(id(dic3['key1'])))
# 深拷貝dic3['key1']的值: [0, 1, 2], 深拷貝dic3['key1'] 的id:2541881346568
# 對於字典中key(鍵)的值是不可變類型的情況
print("原本值dic0['key2']的值: " + str(dic0['key2']) + ", 原本值dic0['key2'] 的id:" + str(id(dic0['key2'])))
# 原本值dic0['key2']的值: 10, 原本值dic0['key2'] 的id:1346137440
print("賦值 dic1['key2']的值: " + str(dic1['key2']) + ", 賦值 dic1['key2'] 的id:" + str(id(dic1['key2'])))
# 賦值 dic1['key2']的值: 10, 賦值 dic1['key2'] 的id:1346137440
print("淺拷貝dic2['key2']的值: " + str(dic2['key2']) + ", 淺拷貝dic2['key2'] 的id:" + str(id(dic2['key2'])))
# 淺拷貝dic2['key2']的值: 10, 淺拷貝dic2['key2'] 的id:1346137440
print("深拷貝dic3['key2']的值: " + str(dic3['key2']) + ", 深拷貝dic3['key2'] 的id:" + str(id(dic3['key2'])))
# 深拷貝dic3['key2']的值: 10, 深拷貝dic3['key2'] 的id:1346137440
# 修改字典
dic0["key1"].remove(1)
dic0["key2"] = "33"
print("修改鍵值後,dic0 的值: " + str(dic0) + ", 修改鍵值後 dic0 的id:" + str(id(dic0)))
# 修改鍵值後,dic0 的值: {'key1': [0, 2], 'key2': '33'}, 修改鍵值後 dic0 的id:2541879095232
print("修改鍵值後,賦值 dic1 的值: " + str(dic1) + ",修改鍵值後 賦值 dic1 的id:" + str(id(dic1)))
# 修改鍵值後,賦值 dic1 的值: {'key1': [0, 2], 'key2': '33'},修改鍵值後 賦值 dic1 的id:2541879095232
print("修改鍵值後,淺拷貝 dic2 的值: " + str(dic2) + ", 修改鍵值後,淺拷貝 dic2 的id:" + str(id(dic2)))
# 修改鍵值後,淺拷貝 dic2 的值: {'key1': [0, 2], 'key2': 10}, 修改鍵值後,淺拷貝 dic2 的id:2541879099464
print("修改鍵值後,深拷貝 dic3 的值: " + str(dic3) + ", 修改鍵值後,深拷貝 dic2 的id:" + str(id(dic3)))
# 修改鍵值後,深拷貝 dic3 的值: {'key1': [0, 1, 2], 'key2': 10}, 修改鍵值後,深拷貝 dic2 的id:2541879972488
# 對於修改字典中(key(鍵)的值是可變類型)的情況
print("修改鍵值後,dic0['key1'] 的值: " + str(dic0['key1']) + ", 修改鍵值後 dic0['key1'] 的id:" + str(id(dic0['key1'])))
# 修改鍵值後,dic0['key1'] 的值: [0, 2], 修改鍵值後 dic0['key1'] 的id:2541881647240
print("修改鍵值後,賦值 dic1['key1'] 的值: " + str(dic1['key1']) + ", 修改鍵值後,賦值 dic1['key1'] 的id:" + str(id(dic1['key1'])))
# 修改鍵值後,賦值 dic1['key1'] 的值: [0, 2], 修改鍵值後,賦值 dic1['key1'] 的id:2541881647240
print("修改鍵值後,淺拷貝 dic2['key1'] 的值: " + str(dic2['key1']) + ", 修改鍵值後,淺拷貝 dic2['key1'] 的id:" + str(id(dic2['key1'])))
# 修改鍵值後,淺拷貝 dic2['key1'] 的值: [0, 2], 修改鍵值後,淺拷貝 dic2['key1'] 的id:2541881647240
print("修改鍵值後,深拷貝 dic3['key1'] 的值: " + str(dic3['key1']) + ", 修改鍵值後,淺拷貝 dic3['key1'] 的id:" + str(id(dic3['key1'])))
# 修改鍵值後,深拷貝 dic3['key1'] 的值: [0, 1, 2], 修改鍵值後,淺拷貝 dic3['key1'] 的id:2541881346568
# 對於修改字典中 (key(鍵)的值是不可變類型)的情況
print("修改鍵值後,dic0['key2'] 的值: " + str(dic0['key2']) + ", 修改鍵值後dic0['key2'] 的id:" + str(id(dic0['key2'])))
# 修改鍵值後,dic0['key2'] 的值: 33, 修改鍵值後dic0['key2'] 的id:2541880133760
print("修改鍵值後,賦值 dic1['key2'] 的值: " + str(dic1['key2']) + ", 修改鍵值後, 賦值 dic1['key2'] 的id:" + str(id(dic1['key2'])))
# 修改鍵值後,dic0['key2'] 的值: 33, 修改鍵值後dic0['key2'] 的id:2541880133760
print("修改鍵值後,淺拷貝 dic2['key2'] 的值: " + str(dic2['key2']) + ", 修改鍵值後,淺拷貝 dic2['key2'] 的id:" + str(id(dic2['key2'])))
# 修改鍵值後,淺拷貝 dic2['key2'] 的值: 10, 修改鍵值後,淺拷貝 dic2['key2'] 的id:1346137440
print("修改鍵值後,深拷貝 dic3['key2'] 的值: " + str(dic3['key2']) + ", 修改鍵值後,深拷貝 dic3['key2'] 的id:" + str(id(dic3['key2'])))
# 修改鍵值後,深拷貝 dic3['key2'] 的值: 10, 修改鍵值後,深拷貝 dic3['key2'] 的id:1346137440
# 綜上所述:字典自帶的 copy 方法可以實現深拷貝 

你可能感興趣的文章

  • 您不知道的Python中global、nonlocal哪些事兒
  • python中break,continue,pass,else的用法和區別詳解

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved