網(wǎng)站首頁 編程語言 正文
前言
copy()與deepcopy()之間的區(qū)分必須要涉及到python對于數(shù)據(jù)的存儲方式。
首先直接上結(jié)論:
- 我們尋常意義的復(fù)制就是深復(fù)制,即將被復(fù)制對象完全再復(fù)制一遍作為獨立的新個體單獨存在。所以改變原有被復(fù)制對象不會對已經(jīng)復(fù)制出來的新對象產(chǎn)生影響。
- 而淺復(fù)制并不會產(chǎn)生一個獨立的對象單獨存在,他只是將原有的數(shù)據(jù)塊打上一個新標簽,所以當(dāng)其中一個標簽被改變的時候,數(shù)據(jù)塊就會發(fā)生變化,另一個標簽也會隨之改變。這就和我們尋常意義上的復(fù)制有所不同了。
對于簡單的 object,用 shallow copy 和 deep copy 沒區(qū)別
復(fù)雜的 object, 如 list 中套著 list 的情況,shallow copy 中的 子list,并未從原 object 真的「獨立」出來。也就是說,如果你改變原 object 的子 list 中的一個元素,你的 copy 就會跟著一起變。這跟我們直覺上對「復(fù)制」的理解不同。
看不懂文字沒關(guān)系我們來看代碼:
>>> 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 更加符合我們對「復(fù)制」的直覺定義: 一旦復(fù)制出來了,就應(yīng)該是獨立的了。如果我們想要的是一個字面意義的「copy」,那就直接用 deep_copy 即可。
那么為什么會有 shallow copy 這樣的「假」 copy 存在呢? 這就是有意思的地方了。
python的數(shù)據(jù)存儲方式
Python 存儲變量的方法跟其他 OOP 語言不同。它與其說是把值賦給變量,不如說是給變量建立了一個到具體值的 reference。
當(dāng)在 Python 中 a = something 應(yīng)該理解為給 something 貼上了一個標簽 a。當(dāng)再賦值給 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] 就相當(dāng)于給這個物品上貼上 a 這個標簽。而 b = a 就是給這個物品又貼上了一個 b 的標簽。
第一種情況:
a = [4, 5, 6] 就相當(dāng)于把 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]。
而在此過程當(dāng)中,a 和 b 都沒有動,他們還貼在那個物品上。因此自然 a b 的值都變成了 [4, 5, 6]。
搞明白這個之后就要問了,對于一個復(fù)雜對象的淺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
學(xué)過docker的人應(yīng)該對鏡像這個概念不陌生,我們可以把鏡像的概念套用在copy上面。
概念圖如下:
copy對于一個復(fù)雜對象的子對象并不會完全復(fù)制,什么是復(fù)雜對象的子對象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是復(fù)雜對象的子對象。對于子對象,python會把它當(dāng)作一個公共鏡像存儲起來,所有對他的復(fù)制都被當(dāng)成一個引用,所以說當(dāng)其中一個引用將鏡像改變了之后另一個引用使用鏡像的時候鏡像已經(jīng)被改變了。
所以說看這里的origin[2]
,也就是[3, 4]
這個 list。根據(jù) shallow copy 的定義,在 cop1[2]
指向的是同一個 list [3, 4]
。那么,如果這里我們改變了這個 list,就會導(dǎo)致 origin 和 cop1 同時改變。這就是為什么上邊 origin[2][0] = “hey!”
之后,cop1 也隨之變成了 [1, 2, [‘hey!’, 4]]。
而deepcopy概念圖如下:
deepcopy的時候會將復(fù)雜對象的每一層復(fù)制一個單獨的個體出來。
這時候的 origin[2] 和 cop2[2] 雖然值都等于 [3, 4],但已經(jīng)不是同一個 list了。即我們尋常意義上的復(fù)制。
原文鏈接:https://blog.csdn.net/qdPython/article/details/126972536
相關(guān)推薦
- 2023-01-12 Android入門之BroadCast模擬實現(xiàn)異地登錄事件發(fā)生后的主動退出_Android
- 2022-11-18 Redux模塊化拆分reducer函數(shù)流程介紹_React
- 2022-05-12 Kotlin any/none/all 函數(shù)
- 2022-12-13 C++?POSIX?API超詳細分析_C 語言
- 2023-04-10 基于Python實現(xiàn)錄音功能的示例代碼_python
- 2022-11-26 React組件如何優(yōu)雅地處理異步數(shù)據(jù)詳解_React
- 2022-04-12 git error: failed to push some refs to
- 2022-09-26 ASP.NET?Core?6最小API中使用日志和DI示例詳解_ASP.NET
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支