python3淺拷貝與深拷貝的區(qū)別和理解

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


首先帚豪,我們知道Python3中雏胃,有6個標準的數(shù)據(jù)類型,他們又分為可變和不可變志鞍。

不可變數(shù)據(jù)(3個):

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

可變數(shù)據(jù)(3個):

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

淺拷貝和深度拷貝 總結

淺拷貝

copy模塊里面的copy方法實現(xiàn)

  • 1瞭亮、對于 不可 變類型 Number String Tuple,淺復制僅僅是地址指向,不會開辟新空間固棚。
  • 2统翩、對于 可 變類型 List、Dictionary此洲、Set厂汗,淺復制會開辟新的空間地址(僅僅是最頂層開辟了新的空間,里層的元素地址還是一樣的)呜师,進行淺拷貝
  • 3娶桦、淺拷貝后,改變原始對象中為可變類型的元素的值汁汗,會同時影響拷貝對象的衷畦;改變原始對象中為不可變類型的元素的值,只有原始類型受影響知牌。(操作拷貝對象對原始對象的也是同理)

深拷貝

copy模塊里面的deepcopy方法實現(xiàn)

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

淺拷貝

  • 1扁藕、對于 不可 變類型 Number String Tuple,淺復制僅僅是地址指向沮峡,不會開辟新空間。
  • 2亿柑、對于 變類型 List邢疙、Dictionary、Set,淺復制會開辟新的空間地址(僅僅是最頂層開辟了新的空間秘症,里層的元素地址還是一樣的)照卦,進行淺拷貝
  • 3、淺拷貝后乡摹,改變原始對象中為可變類型的元素的值役耕,會同時影響拷貝對象的;改變原始對象中為不可變類型的元素的值聪廉,只有原始類型受影響瞬痘。 (操作拷貝對象對原始對象的也是同理)

可變類型和不可變類型在淺拷貝中的區(qū)別

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]


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

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

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

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亚脆、經(jīng)過深拷貝后做院,原始對象和拷貝對象所有的子元素地址都是獨立的了
  • 3、可以用分片表達式進行深拷貝
  • 4濒持、字典的copy方法可以拷貝一個字典

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

我們對3種可變類型3種不可變類型進行深拷貝键耕。
結果發(fā)現(xiàn),和淺拷貝幾乎一致弥喉。

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

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]


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

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,還有其他自帶的方式可實現(xiàn)拷貝。

  • 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方法可實現(xiàn)深拷貝

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

本文完蒜撮,謝謝閱讀。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末慷嗜,一起剝皮案震驚了整個濱河市淀弹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌庆械,老刑警劉巖薇溃,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缭乘,居然都是意外死亡沐序,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門堕绩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來策幼,“玉大人,你說我怎么就攤上這事奴紧√亟悖” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵黍氮,是天一觀的道長唐含。 經(jīng)常有香客問我,道長沫浆,這世上最難降的妖魔是什么捷枯? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮专执,結果婚禮上淮捆,老公的妹妹穿的比我還像新娘。我一直安慰自己本股,他們只是感情好攀痊,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拄显,像睡著了一般蚕苇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凿叠,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音,去河邊找鬼盒件。 笑死蹬碧,一個胖子當著我的面吹牛,可吹牛的內容都是我干的炒刁。 我是一名探鬼主播恩沽,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼翔始!你這毒婦竟也來了罗心?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤城瞎,失蹤者是張志新(化名)和其女友劉穎渤闷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脖镀,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡飒箭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蜒灰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弦蹂。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖强窖,靈堂內的尸體忽然破棺而出凸椿,到底是詐尸還是另有隱情,我是刑警寧澤翅溺,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布脑漫,位于F島的核電站,受9級特大地震影響未巫,放射性物質發(fā)生泄漏窿撬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一叙凡、第九天 我趴在偏房一處隱蔽的房頂上張望劈伴。 院中可真熱鬧,春花似錦握爷、人聲如沸墩邀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽范咨。三九已至,卻和暖如春燥撞,著一層夾襖步出監(jiān)牢的瞬間座柱,已是汗流浹背迷帜。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留色洞,地道東北人戏锹。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像火诸,于是被迫代替她去往敵國和親锦针。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355