copy()與deepcopy()之間的區(qū)分必須要涉及到python對于數(shù)據(jù)的存儲方式傅物。
首先直接上結論:
—–我們尋常意義的復制就是深復制顷链,即將被復制對象完全再復制一遍作為獨立的新個體單獨存在目代。所以改變原有被復制對象不會對已經(jīng)復制出來的新對象產(chǎn)生影響。
—–而淺復制并不會產(chǎn)生一個獨立的對象單獨存在嗤练,他只是將原有的數(shù)據(jù)塊打上一個新標簽榛了,所以當其中一個標簽被改變的時候,數(shù)據(jù)塊就會發(fā)生變化煞抬,另一個標簽也會隨之改變霜大。這就和我們尋常意義上的復制有所不同了。對于簡單的 object革答,用 shallow copy 和 deep copy 沒區(qū)別
復雜的 object战坤, 如 list 中套著 list 的情況遮婶,shallow copy 中的 子list,并未從原 object 真的「獨立」出來湖笨。也就是說旗扑,如果你改變原 object 的子 list 中的一個元素,你的 copy 就會跟著一起變慈省。這跟我們直覺上對「復制」的理解不同臀防。
>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin 里邊有三個元素:1, 2边败,[3, 4]
>>> cop1 = copy.copy(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False
#cop1 和 cop2 看上去相同袱衷,但已不再是同一個object
>>> origin[2][0] = "hey!"
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
#把origin內(nèi)的子list [3, 4] 改掉了一個元素,觀察 cop1 和 cop2
可以看到 cop1笑窜,也就是 shallow copy 跟著 origin 改變了致燥。而 cop2 ,也就是 deep copy 并沒有變排截。
似乎 deep copy 更加符合我們對「復制」的直覺定義: 一旦復制出來了嫌蚤,就應該是獨立的了。如果我們想要的是一個字面意義的「copy」断傲,那就直接用 deep_copy 即可脱吱。
那么為什么會有 shallow copy 這樣的「假」 copy 存在呢? 這就是有意思的地方了认罩。
python的數(shù)據(jù)存儲方式
Python 存儲變量的方法跟其他 OOP 語言不同箱蝠。它與其說是把值賦給變量,不如說是給變量建立了一個到具體值的 reference垦垂。
當在 Python 中 a = something 應該理解為給 something 貼上了一個標簽 a宦搬。當再賦值給 a 的時候,就好象把 a 這個標簽從原來的 something 上拿下來劫拗,貼到其他對象上间校,建立新的 reference。 這就解釋了一些 Python 中可能遇到的詭異情況:
>> a = [1, 2, 3]
>>> b = a
>>> a = [4, 5, 6] //賦新的值給 a
>>> a
[4, 5, 6]
>>> b
[1, 2, 3]
# a 的值改變后杨幼,b 并沒有隨著 a 變
>>> a = [1, 2, 3]
>>> b = a
>>> a[0], a[1], a[2] = 4, 5, 6 //改變原來 list 中的元素
>>> a
[4, 5, 6]
>>> b
[4, 5, 6]
# a 的值改變后撇簿,b 隨著 a 變了
上面兩段代碼中,a 的值都發(fā)生了變化差购。區(qū)別在于,第一段代碼中是直接賦給了 a 新的值(從 [1, 2, 3] 變?yōu)?[4, 5, 6])汉嗽;而第二段則是把 list 中每個元素分別改變欲逃。
而對 b 的影響則是不同的,一個沒有讓 b 的值發(fā)生改變饼暑,另一個變了稳析。怎么用上邊的道理來解釋這個詭異的不同呢洗做?
首次把 [1, 2, 3] 看成一個物品。a = [1, 2, 3] 就相當于給這個物品上貼上 a 這個標簽彰居。而 b = a 就是給這個物品又貼上了一個 b 的標簽诚纸。
第一種情況:
a = [4, 5, 6] 就相當于把 a 標簽從 [1 ,2, 3] 上撕下來,貼到了 [4, 5, 6] 上陈惰。
在這個過程中畦徘,[1, 2, 3] 這個物品并沒有消失。 b 自始至終都好好的貼在 [1, 2, 3] 上抬闯,既然這個 reference 也沒有改變過井辆。 b 的值自然不變。
第二種情況:
a[0], a[1], a[2] = 4, 5, 6 則是直接改變了 [1, 2, 3] 這個物品本身溶握。把它內(nèi)部的每一部分都重新改裝了一下杯缺。內(nèi)部改裝完畢后,[1, 2, 3] 本身變成了 [4, 5, 6]睡榆。
而在此過程當中萍肆,a 和 b 都沒有動,他們還貼在那個物品上胀屿。因此自然 a b 的值都變成了 [4, 5, 6]匾鸥。
搞明白這個之后就要問了,對于一個復雜對象的淺copy碉纳,在copy的時候到底發(fā)生了什么勿负?
再看一段代碼:
>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin 里邊有三個元素:1, 2劳曹,[3, 4]
>>> cop1 = copy.copy(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False
#cop1 和 cop2 看上去相同奴愉,但已不再是同一個object
>>> origin[2][0] = "hey!"
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
#把origin內(nèi)的子list [3, 4] 改掉了一個元素,觀察 cop1 和 cop2
我們可以把鏡像的概念套用在copy上面铁孵。
概念圖如下:
copy對于一個復雜對象的子對象并不會完全復制锭硼,什么是復雜對象的子對象呢?就比如序列里的嵌套序列蜕劝,字典里的嵌套序列等都是復雜對象的子對象檀头。對于子對象,python會把它當作一個公共鏡像存儲起來岖沛,所有對他的復制都被當成一個引用暑始,所以說當其中一個引用將鏡像改變了之后另一個引用使用鏡像的時候鏡像已經(jīng)被改變了。
所以說看這里的origin[2]婴削,也就是 [3, 4] 這個 list廊镜。根據(jù) shallow copy 的定義,在 cop1[2] 指向的是同一個 list [3, 4]唉俗。那么嗤朴,如果這里我們改變了這個 list配椭,就會導致 origin 和 cop1 同時改變。這就是為什么上邊 origin[2][0] = “hey!” 之后雹姊,cop1 也隨之變成了 [1, 2, [‘hey!’, 4]]股缸。
而deepcopy概念圖如下:
deepcopy的時候會將復雜對象的每一層復制一個單獨的個體出來。
這時候的 origin[2] 和 cop2[2] 雖然值都等于 [3, 4]吱雏,但已經(jīng)不是同一個 list了敦姻。即我們尋常意義上的復制。
參考文章:https://blog.csdn.net/qq_32907349/article/details/52190796