文/阿敏其人
本文出自“阿敏其人”簡書博客,轉載請取得本人同意。
首先,我們知道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
本文完,謝謝閱讀。