python為拷貝提供了copy模塊。提供了兩種主要的copy方法,一種是普通的copy,另一種是deepcopy。我們稱前者是淺拷貝,后者為深拷貝。
深淺拷貝一直是所有編程語(yǔ)言的重要知識(shí)點(diǎn),下面我們就從內(nèi)存的角度來(lái)分析一下兩者的區(qū)別。
一、淺拷貝
首先,我們來(lái)了解一下淺拷貝。淺拷貝:不管多么復(fù)雜的數(shù)據(jù)結(jié)構(gòu),淺拷貝都只會(huì)copy一層。下面就讓我們看一張圖,來(lái)了解一下淺淺拷貝的概念。


看上面兩張圖,我們加入左圖表示的是一個(gè)列表sourcelist,sourcelist = ['str1','str2','str3','str4','str5',['str1','str2','str3','str4','str5']];
右圖在原有的基礎(chǔ)上多出了一個(gè)淺拷貝的copylist,copylist = ['str1','str2','str3','str4','str5',['str1','str2','str3','str4','str5']];
sourcelist和copylist表面上看起來(lái)一模一樣,但是實(shí)際上在內(nèi)存中已經(jīng)生成了一個(gè)新列表,copy了sourceLst,獲得了一個(gè)新列表,存儲(chǔ)了5個(gè)字符串和一個(gè)列表所在內(nèi)存的地址。
我們看下面分別對(duì)兩個(gè)列表進(jìn)行的操作,紅色的框框里面是變量初始化,初始化了上面的兩個(gè)列表;我們可以分別對(duì)這兩個(gè)列表進(jìn)行操作,例如插入一個(gè)值,我們會(huì)發(fā)現(xiàn)什么呢?如下所示:

從上面的代碼我們可以看出,對(duì)于sourceLst和copyLst列表添加一個(gè)元素,這兩個(gè)列表好像是獨(dú)立的一樣都分別發(fā)生了變化,但是當(dāng)我修改lst的時(shí)候,這兩個(gè)列表都發(fā)生了變化,這是為什么呢?我們就來(lái)看一張內(nèi)存中的變化圖:

我們可以知道sourceLst和copyLst列表中都存儲(chǔ)了一坨地址,當(dāng)我們修改了sourceLst1的元素時(shí),相當(dāng)于用'sourceChange'的地址替換了原來(lái)'str1'的地址,所以sourceLst的第一個(gè)元素發(fā)生了變化。而copyLst還是存儲(chǔ)了str1的地址,所以copyLst不會(huì)發(fā)生改變。
當(dāng)sourceLst列表發(fā)生變化,copyLst中存儲(chǔ)的lst內(nèi)存地址沒(méi)有改變,所以當(dāng)lst發(fā)生改變的時(shí)候,sourceLst和copyLst兩個(gè)列表就都發(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一份并且它們之間又沒(méi)有一毛錢關(guān)系,應(yīng)該怎么辦呢?
我們引入一個(gè)深拷貝的概念,深拷貝——即python的copy模塊提供的另一個(gè)deepcopy方法。深拷貝會(huì)完全復(fù)制原變量相關(guān)的所有數(shù)據(jù),在內(nèi)存中生成一套完全一樣的內(nèi)容,在這個(gè)過(guò)程中我們對(duì)這兩個(gè)變量中的一個(gè)進(jìn)行任意修改都不會(huì)影響其他變量。下面我們就來(lái)試驗(yàn)一下。