python3淺拷貝與深拷貝的區別和理解

文/阿敏其人
本文出自“阿敏其人”簡書博客,轉載請取得本人同意。


首先,我們知道Python3中,有6個標準的數據類型,他們又分為可變和不可變。

不可變數據(3個):

  • Number(數字)
  • String(字符串)
  • Tuple(元組)

可變數據(3個):

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

淺拷貝和深度拷貝 總結

淺拷貝

copy模塊里面的copy方法實現

  • 1、對于 不可 變類型 Number String Tuple,淺復制僅僅是地址指向,不會開辟新空間。
  • 2、對于 可 變類型 List、Dictionary、Set,淺復制會開辟新的空間地址(僅僅是最頂層開辟了新的空間,里層的元素地址還是一樣的),進行淺拷貝
  • 3、淺拷貝后,改變原始對象中為可變類型的元素的值,會同時影響拷貝對象的;改變原始對象中為不可變類型的元素的值,只有原始類型受影響。(操作拷貝對象對原始對象的也是同理)

深拷貝

copy模塊里面的deepcopy方法實現

  • 1、淺拷貝,除了頂層拷貝,還對子元素也進行了拷貝(本質上遞歸淺拷貝)
  • 2、經過深拷貝后,原始對象和拷貝對象所有的元素地址都沒有相同的了

淺拷貝

  • 1、對于 不可 變類型 Number String Tuple,淺復制僅僅是地址指向,不會開辟新空間。
  • 2、對于 變類型 List、Dictionary、Set,淺復制會開辟新的空間地址(僅僅是最頂層開辟了新的空間,里層的元素地址還是一樣的),進行淺拷貝
  • 3、淺拷貝后,改變原始對象中為可變類型的元素的值,會同時影響拷貝對象的;改變原始對象中為不可變類型的元素的值,只有原始類型受影響。 (操作拷貝對象對原始對象的也是同理)

可變類型和不可變類型在淺拷貝中的區別

import copy

# 不可變類型 Number String Tuple
print("對于不可 變類型 Number String Tuple,淺復制僅僅是地址指向,不會開辟新空間拷貝值")

num1 = 17
num2 = copy.copy(num1)

print("num1:" + str(id(num1)))
print("num2:" + str(id(num1)))
# num1和num2的地址都相同


str1 = "hello"
str2 = copy.copy(str1)
print("str1:" + str(id(str1)))
print("str2:" + str(id(str2)))
# str1和str2的地址都相同

tup1 = (18, "tom")
tup2 = copy.copy(tup1)
print("tup1:" + str(id(tup1)))
print("tup2:" + str(id(tup2)))
# tup1和tup2的地址都相同

print("="*20)
print("對于可變類型 List、Dictionary、Set,淺復制會開辟新的空間地址(僅僅是最頂層開辟了新的空間),進行淺拷貝")


list1 = [11,12]
list2 = copy.copy(list1)
print("list1:" + str(id(list1)))
print("list2:" + str(id(list2)))
# list1和list2的地址不相同


dic1 = [11,12,"hi"]
dic2 = copy.copy(dic1)
print("dic1:" + str(id(dic1)))
print("dic2:" + str(id(dic2)))
# dic1和dic2的地址不相同

set1 = {"AA","BB"}
set2 = copy.copy(set1)
print("set1:" + str(id(set1)))
print("set2:" + str(id(set2)))
# set1和set2的地址不相同

.
.
輸出:

對于不可 變類型 Number String Tuple,淺復制僅僅是地址指向,不會開辟新空間拷貝值
num1:4449693616
num2:4449693616
str1:4452098488
str2:4452098488
tup1:4451942472
tup2:4451942472
====================
對于可變類型 List、Dictionary、Set,淺復制會開辟新的空間地址,進行淺拷貝
list1:4456844424
list2:4452360136
dic1:4452358856
dic2:4456844744
set1:4452279016
set2:4452279464

對list進淺拷貝,對可變類型和不可變類型修改后的影響。

import copy

l1 = [11, 12]
l2 = [21, 22]
num = 555

allOne = [l1, l2,num]


# 淺拷貝,創建出一個對象,并把舊對象元素的 引用地址 拷貝到新對象當中。
# 也就是說,兩個對象里面的元素通過淺拷貝指向的還是同一個地址
allOne2 = copy.copy(allOne)

l1[0] = 16 # 此處修改,會使得 allOne 和 allOne2的第0個元素的值都發生改變,因為l1是List,是可變對象
allOne[2] = 666 # 此處修改,只會allOne的num的值,因為不可變對象一旦重新復制,地址就會發生改變。(不可變嘛)

num = 777 # 此處不會改變 allOne 和 allOne2的值,因為相當于 777 復制給一個全新的地址,這個num跟其他num已經沒關系了

print(allOne)
print(allOne2)

print("id allOne:"+str(id(allOne)))
print("id allOne[0]:"+str(id(allOne[0])))
print("id allOne[1]:"+str(id(allOne[1])))
print("id allOne[2]:"+str(id(allOne[2])))

print("===")
print("id allOne2:"+str(id(allOne2)))
print("id allOne2[0]:"+str(id(allOne2[0])))
print("id allOne2[1]:"+str(id(allOne2[1])))
print("id allOne2[2]:"+str(id(allOne2[2])))

.
.
打印輸出

[[16, 12], [21, 22], 666]
[[16, 12], [21, 22], 555]
id allOne:4467341640
id allOne[0]:4471819912
id allOne[1]:4467342920
id allOne[2]:4466847696
===
id allOne2:4471820232
id allOne2[0]:4471819912
id allOne2[1]:4467342920
id allOne2[2]:4466081744

可以看出:

  • 改動allOne中的可變類型,會影響allOne2,改變allOne2同理影響allOne。
  • 改動allOne2中的不可變類型,只有allOne2自身會改變,allOne不受影響。

(List是可變類型)

.
.

對于不可變類型被修改后造成的影響,我們用一個更加簡單的例子便可更好理解:

num = 123
print(str(id(num)))

num = 666
print(str(id(num)))

.
.
console:

4348603632
4350009296

幾乎可以說,Python 沒有"變量",我們平時所說的變量其實只是"標簽",是引用。
關于 = 符號,可以參考 python基礎(5):深入理解 python 中的賦值、引用、拷貝、作用域

深拷貝

  • 1、淺拷貝,除了頂層拷貝,還對子元素也進行了拷貝(本質上遞歸淺拷貝)
  • 2、經過深拷貝后,原始對象和拷貝對象所有的子元素地址都是獨立的了
  • 3、可以用分片表達式進行深拷貝
  • 4、字典的copy方法可以拷貝一個字典

深拷貝對6種基本類型的影響

我們對3種可變類型3種不可變類型進行深拷貝。
結果發現,和淺拷貝幾乎一致。

其實這也好理解,因為的深拷貝對比淺拷貝,強調的是 遞歸,強調的是資源素。
對了頂層的操作,深淺拷貝無異。

import copy

# 不可變類型 Number String Tuple
print("對于不可 變類型 Number String Tuple,深復制依然是地址指向,不會開辟新空間拷貝值")

num1 = 17
num2 = copy.deepcopy(num1) # 深拷貝

print("num1:" + str(id(num1)))
print("num2:" + str(id(num1)))
# num1和num2的地址都相同


str1 = "hello"
str2 = copy.deepcopy(str1)  # 深拷貝
print("str1:" + str(id(str1)))
print("str2:" + str(id(str2)))
# str1和str2的地址都相同

tup1 = (18, "tom")
tup2 = copy.deepcopy(tup1) # 深拷貝
print("tup1:" + str(id(tup1)))
print("tup2:" + str(id(tup2)))
# tup1和tup2的地址都相同

print("="*20)
print("對于可變類型 List、Dictionary、Set,深拷貝會開辟新的空間地址,進行拷貝")


list1 = [11,12]
list2 = copy.deepcopy(list1) # 深拷貝
print("list1:" + str(id(list1)))
print("list2:" + str(id(list2)))
# list1和list2的地址不相同


dic1 = [11,12,"hi"]
dic2 = copy.deepcopy(dic1) # 深拷貝
print("dic1:" + str(id(dic1)))
print("dic2:" + str(id(dic2)))
# dic1和dic2的地址不相同

set1 = {"AA","BB"}
set2 = copy.deepcopy(set1) # 深拷貝
print("set1:" + str(id(set1)))
print("set2:" + str(id(set2)))
# set1和set2的地址不相同


深拷貝的會對子元素也進行拷貝

import copy

l1 = [11, 12]
l2 = [21, 22]
num = 555

allOne = [l1, l2,num]


# 淺拷貝,除了頂層拷貝,還對子元素也進行了拷貝(本質上遞歸淺拷貝)
# 經過深拷貝后,原始對象和拷貝對象所有的元素地址都沒有相同的了

allOne2 = copy.deepcopy(allOne) # copy.deepcopy 深拷貝

allOne[1] = [113,114]
allOne2[2] = [227,228]

print(allOne)
print(allOne2)

print("id allOne:"+str(id(allOne)))
print("id allOne[0]:"+str(id(allOne[0])))
print("id allOne[1]:"+str(id(allOne[1])))
print("id allOne[2]:"+str(id(allOne[2])))

print("===")
print("id allOne2:"+str(id(allOne2)))
print("id allOne2[0]:"+str(id(allOne2[0])))
print("id allOne2[1]:"+str(id(allOne2[1])))
print("id allOne2[2]:"+str(id(allOne2[2])))


.
.
console

[[11, 12], [113, 114], 555]
[[11, 12], [21, 22], [227, 228]]
id allOne:4549589640
id allOne[0]:4554067720
id allOne[1]:4554067848
id allOne[2]:4548329424
===
id allOne2:4554067912
id allOne2[0]:4554067784
id allOne2[1]:4554067592
id allOne2[2]:4554100808

本例是跟淺拷貝做對比的。
在之前的淺拷貝中,子元素是不會開辟新空間做拷貝的。
而在深拷貝中,子元素也進行了拷貝。


其他拷貝方式

除了copy模塊的中的copy和deepcopy,還有其他自帶的方式可實現拷貝。

  • 1、分片表達式進行淺拷貝
  • 2、字典的copy方法可以拷貝一個字典

分片表達式拷貝

l1 = [11, 12]
l2 = [21, 22]
num = 555

orgi = [l1, l2, num]

nList = orgi[:]

print("orgi:"+str(id(orgi)))
print("orgi[0]:"+str(id(orgi[0])))
print("orgi[1]:"+str(id(orgi[1])))
print("orgi[2]:"+str(id(orgi[2])))
print("*"*30)
print("nList:"+str(id(nList)))
print("nList[0]:"+str(id(nList[0])))
print("nList[1]:"+str(id(nList[1])))
print("nList[2]:"+str(id(nList[2])))

用分片表達式進行的拷貝,是淺拷貝。

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

dic = {"key": "hello", "num": 18}

dic2 = dic.copy()

dic["key"] = "one"
dic2["key"] = "two"

print(dic)
print("dic:" + str(id(dic)))

print(dic2)
print("dic2:" + str(id(dic2)))

console:

{'key': 'one', 'num': 18}
dic:4382946792
{'key': 'two', 'num': 18}
dic2:4382946864

本文完,謝謝閱讀。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。