我們已經(jīng)詳細(xì)了解了變量賦值的過程点待。對于復(fù)雜的數(shù)據(jù)結(jié)構(gòu)來說,賦值就等于完全共享了資源弃舒,一個值的改變會完全被另一個值共享癞埠。
然而有的時(shí)候,我們偏偏需要將一份數(shù)據(jù)的原始內(nèi)容保留一份,再去處理數(shù)據(jù)燕差,這個時(shí)候使用賦值就不夠明智了。python為這種需求提供了copy模塊坝冕。提供了兩種主要的copy方法徒探,一種是普通的copy,另一種是deepcopy喂窟。我們稱前者是淺拷貝测暗,后者為深拷貝。
深淺拷貝一直是所有編程語言的重要知識點(diǎn)磨澡,下面我們就從內(nèi)存的角度來分析一下兩者的區(qū)別碗啄。
? ? ? ?首先,我們來了解一下淺拷貝稳摄。淺拷貝:不管多么復(fù)雜的數(shù)據(jù)結(jié)構(gòu)稚字,淺拷貝都只會copy一層。下面就讓我們看一張圖厦酬,來了解一下淺淺拷貝的概念胆描。
?看上面兩張圖,我們加入左圖表示的是一個列表sourcelist仗阅,sourcelist =?['str1','str2','str3','str4','str5',['str1','str2','str3','str4','str5']]昌讲;
右圖在原有的基礎(chǔ)上多出了一個淺拷貝的copylist,copylist =?['str1','str2','str3','str4','str5',['str1','str2','str3','str4','str5']]减噪;
sourcelist和copylist表面上看起來一模一樣短绸,但是實(shí)際上在內(nèi)存中已經(jīng)生成了一個新列表,copy了sourceLst筹裕,獲得了一個新列表醋闭,存儲了5個字符串和一個列表所在內(nèi)存的地址。
? ? ? 我們看下面分別對兩個列表進(jìn)行的操作朝卒,紅色的框框里面是變量初始化目尖,初始化了上面的兩個列表;我們可以分別對這兩個列表進(jìn)行操作扎运,例如插入一個值瑟曲,我們會發(fā)現(xiàn)什么呢?如下所示:
? ? ? ?從上面的代碼我們可以看出豪治,對于sourceLst和copyLst列表添加一個元素洞拨,這兩個列表好像是獨(dú)立的一樣都分別發(fā)生了變化,但是當(dāng)我修改lst的時(shí)候负拟,這兩個列表都發(fā)生了變化烦衣,這是為什么呢?我們就來看一張內(nèi)存中的變化圖:
我們可以知道sourceLst和copyLst列表中都存儲了一坨地址,當(dāng)我們修改了sourceLst1的元素時(shí)花吟,相當(dāng)于用'sourceChange'的地址替換了原來'str1'的地址秸歧,所以sourceLst的第一個元素發(fā)生了變化。而copyLst還是存儲了str1的地址衅澈,所以copyLst不會發(fā)生改變键菱。
當(dāng)sourceLst列表發(fā)生變化,copyLst中存儲的lst內(nèi)存地址沒有改變今布,所以當(dāng)lst發(fā)生改變的時(shí)候经备,sourceLst和copyLst兩個列表就都發(fā)生了改變。
這種情況發(fā)生在字典套字典部默、列表套字典侵蒙、字典套列表,列表套列表傅蹂,以及各種復(fù)雜數(shù)據(jù)結(jié)構(gòu)的嵌套中纷闺,所以當(dāng)我們的數(shù)據(jù)類型很復(fù)雜的時(shí)候,用copy去進(jìn)行淺拷貝就要非常小心份蝴。急但。。
剛剛我們了解了淺拷貝的意義搞乏,但是在寫程序的時(shí)候波桩,我們就是希望復(fù)雜的數(shù)據(jù)結(jié)構(gòu)之間完全copy一份并且它們之間又沒有一毛錢關(guān)系,應(yīng)該怎么辦呢请敦?
我們引入一個深拷貝的概念镐躲,深拷貝——即python的copy模塊提供的另一個deepcopy方法。深拷貝會完全復(fù)制原變量相關(guān)的所有數(shù)據(jù)侍筛,在內(nèi)存中生成一套完全一樣的內(nèi)容萤皂,在這個過程中我們對這兩個變量中的一個進(jìn)行任意修改都不會影響其他變量。下面我們就來試驗(yàn)一下匣椰。
? ? ? 看上面的執(zhí)行結(jié)果裆熙,這一次我們不管是對直接對列表進(jìn)行操作還是對列表內(nèi)嵌套的其他數(shù)據(jù)結(jié)構(gòu)操作,都不會產(chǎn)生拷貝的列表受影響的情況禽笑。我們再來看看這些變量在內(nèi)存中的狀況:
看了上面的內(nèi)容入录,我們就知道了深拷貝的原理。其實(shí)深拷貝就是在內(nèi)存中重新開辟一塊空間佳镜,不管數(shù)據(jù)結(jié)構(gòu)多么復(fù)雜僚稿,只要遇到可能發(fā)生改變的數(shù)據(jù)類型,就重新開辟一塊內(nèi)存空間把內(nèi)容復(fù)制下來蟀伸,直到最后一層蚀同,不再有復(fù)雜的數(shù)據(jù)類型缅刽,就保持其原引用。這樣蠢络,不管數(shù)據(jù)結(jié)構(gòu)多么的復(fù)雜衰猛,數(shù)據(jù)之間的修改都不會相互影響。這就是深拷貝