Python 拓展之詳解深拷貝和淺拷貝

2021-01-10 Python空間

寫在之前

在昨天的文章裡 (零基礎學習 Python 之字典),寫字典的方法的時候留了一個小尾巴,那就是 copy() 方法還沒講。一是因為 copy 這個方法比較特殊,不單單是它表面的意思;二是以為昨天的文章寫得比較長,可能你看到那的時候就沒啥耐心去仔細思考了,但是這個知識點又比較重要,也是面試過程中會被長問起的題,我之前在面試的時候(乾貨滿滿--親身經歷的 Python 面試題)就被問起過。所以我把 copy 單獨摘出來今天單講。

正式開始

首先我在這介紹兩個新的小知識,要在下面用到。一個是函數 id() ,另一個是運算符 is。id() 函數就是返回對象的內存地址;is 是比較兩個變量的對象引用是否指向同一個對象,在這裡請不要和 == 混了,== 是比較兩個變量的值是否相等。

>>> a = [1,2,3]>>> b = [1,2,3]>>> id(a)38884552L>>> a is bFalse>>> a == bTrue

copy 這個詞有兩種叫法,一種是根據它的發音音譯過來的,叫拷貝;另一種就是標準的翻譯,叫複製。

其實單從表面意思來說,copy 就是將某件東西再複製一份,但是在很多程式語言中,比如 Python,C++中,它就不是那麼的簡單了。

>>> a = 1>>> b = a>>> b1

看到上面的例子,從表面上看我們似乎是得到了兩個 1,但是如果你看過我之前寫的文章,你應該對一句話有印象,那就是 「變量無類型」, Python 中變量就是一個標籤,這裡我們有請 id() 閃亮登場,看看它們在內存中的位置。

>>> a = 1>>> b = a>>> b1>>> id(a)31096808L>>> id(b)31096808L

看出來了嗎,id(a) 和 id(b) 相等,所以並沒有兩個 1,只是一個 1 而已,只不過是在 1 上貼了兩張標籤,名字是 a 和 b 罷了,這種現象普遍存在於 Python 之中,這種賦值的方式實現了 「假裝」 拷貝,真實的情況還是兩個變量和同一個對象之間的引用關係。

我們再來看 copy() 方法:

>>> a = {'name':'rocky','like':'python'}

>>> b = a.copy()>>> b{'name': 'rocky', 'like': 'python'}>>> id(a)31036280L>>> id(b)38786728L

咦,果然這次得到的 b 和原來的 a 不同,它是在內存中又開闢了一個空間。那麼我們這個時候就來推理了,雖然它們兩個是一樣的,但是它們在兩個不同的內存空間裡,那麼肯定彼此互不幹擾,如果我們去把 b 改了,那麼 a 肯定不變。

>>> b['name'] = 'leey'

>>> b{'name': 'leey', 'like': 'python'}>>> a{'name': 'rocky', 'like': 'python'}

結果和我們上面推理的一模一樣,所以理解了對象有類型,變量無類型,變量是對象的標籤,就能正確推斷出 Python 提供的結果。

我們接下來在看一個例子,請你在往下看的時候保證上面的你已經懂了,不然容易暈車。

>>> a = {'name':'rocky','like':'python'}

>>> b = a>>> b{'name': 'rocky', 'like': 'python'}>>> b['name'] = 'leey'>>> b{'name': 'leey', 'like': 'python'}>>> a{'name': 'leey', 'like': 'python'}

上面的例子看出什麼來了嗎?修改了 b 對應的字典類型的對象,a 的對象也變了。也就是說, b = a 得到的結果是兩個變量引用了同一個對象,但是事情真的這麼簡單嗎?請睜大你的眼睛往下看,重點來了。

>>> first = {'name':'rocky','lanaguage':['python','c++','java']}

>>> second = first.copy()>>> second{'name': 'rocky', 'lanaguage': ['python', 'c++', 'java']}>>> id(first)31036280L>>> id(second)38786728L

在這裡的話沒有問題,和我們之前說的一樣,second 是從 first 拷貝過來的,它們分別引用的是兩個對象。

>>> second['lanaguage'].remove('java')

>>> second{'name': 'rocky', 'lanaguage': ['python', 'c++']}>>> first{'name': 'rocky', 'lanaguage': ['python', 'c++']}

發現什麼了嗎?按理說上述例子中 second 的 lanaguage 對應的是一個列表,我刪除這個列表裡的值,也只應該改變的是 second 啊,為什麼連 first 的也會改,不是應該互不幹擾嗎?是不是很意外?是我們之前說的不對嗎?那我們再試試另一個鍵:

>>> second['name'] = 'leey'

>>> second{'name': 'leey', 'lanaguage': ['python', 'c++']}>>> first{'name': 'rocky', 'lanaguage': ['python', 'c++']}

前面說的原理是有效的,那這到底是為什麼啊,來來來,有請我們的 id() 再次閃亮登場。

>>> id(first['name'])38829152L>>> id(second['name'])38817544L>>> id(first['lanaguage'])38754120L>>> id(second['lanaguage'])38754120L

其實這裡深層次的原因是和 Python 的存儲數據的方式有關,這裡不做過多的說明(其實是我也不懂。。 在這裡,我們只需要知道的是,當 copy() 的時候,列表這類由字符串,數字等複合而成的對象仍然是複製了引用,也就是貼標籤,並沒有建立一個新的對象,我們把這種拷貝方式叫做淺拷貝(唉呀媽呀,終於把這個概念引出來了。。,言外之意就是並沒有解決深層次的問題,再言外之意就是還有能夠解決深層次問題的方法。

確實,在 Python 中還有一個深拷貝(deep copy),在使用它之前要引入一個 copy 模塊,我們來試一下。

>>> import copy>>> first = {'name':'rocky','lanaguage':['python','c++','java']}>>> second = copy.deepcopy(first)>>> second{'name': 'rocky', 'lanaguage': ['python', 'c++', 'java']}>>> second['lanaguage'].remove('java')>>> second{'name': 'rocky', 'lanaguage': ['python', 'c++']}>>> first{'name': 'rocky', 'lanaguage': ['python', 'c++', 'java']}

用了深拷貝以後,果然就不是引用了。

寫在最後

深拷貝和淺拷貝到這裡就講完了,花了一番功夫總算寫的還令自己滿意,不知道朋友們看到這裡的時候是否是覺得對這一部分豁然開朗,我盡力了。這個拓展也可能是成為一個系列,補充一些我覺得理解起來比較困難或者平時面試求職或者工作中常見的知識點,希望您多捧場。

最後感謝你能看到這裡,希望我寫的東西能夠讓你有到收穫,但是我還是希望我在文章裡插入的代碼,你們能自己動手試一下,都很簡單。原創不易,每一個字,每一個標點都是自己手敲的,所以希望大家能多給點支持,該關注關注,該點讚點讚,該轉發轉發,有什麼問題歡迎在後臺聯繫我,也可以在公眾號找到我的微信加我。

我這麼認真你還不趕緊走一波關注嗎??

The end。

相關焦點

  • Python 深拷貝、淺拷貝
    淺拷貝淺拷貝是對於一個對象的頂層拷貝通俗的理解是:拷貝了引用,並沒有拷貝內容2. 深拷貝深拷貝是對於一個對象所有層次的拷貝(遞歸)進一步理解深拷貝3. 拷貝的其他方式分片表達式可以賦值一個序列字典的copy方法可以拷貝一個字典4.
  • Python中又一對有趣的功能:淺拷貝與深拷貝
    先把淺拷貝與深拷貝的概念拋出來:淺拷貝:不考貝子對象的內容,只拷貝子對象的引用。調用Python內置函數copy(源對象)。深拷貝:子對象的內存也會被拷貝一份,並且對子對象的修改不會影響源對象。調用Python內置函數deepcopy(源對象)。
  • python中深淺拷貝
    大家在面試的時候經常會遇到一個這樣的問題:請問python中的深淺拷貝有什麼區別?下邊就簡單給大家總結一下這個問題:一、直接賦值拷貝,比如:a=b(1)數字和字符串在內存中是同一塊地址(2)字典、列表、元組等其他類型也是同一塊內存地址,不發生變化
  • 一步一步帶你理解 Python 中的淺複製與深複製
    需要注意的是,對於複合類型的對象,比如列表、字典、集合等,複製有淺複製與深複製兩種類型。淺複製意味著新建一個對象,但是其子元素仍然指向的對應原對象的子對象。也就是說,這只會對原對象進行一層的拷貝,而不會遞歸的對子對象也進行拷貝。深複製則會遞歸的對子對象進行拷貝。如果上面的看不懂,沒關係,我們通過一個個例子來搞清楚。
  • JS之函數傳參與深淺拷貝原理
    很多問題看似複雜,沒有章法,事實上卻有著千絲萬縷的聯繫,陳道長此次闡述因為數據類型不同而引發的問題,本文主要探討JS函數參數傳遞規則、淺拷貝、深拷貝的原理。變量類型和存儲首先要明確js中變量的特點,JS變量本身沒有類型,只有值有類型。這句話怎麼理解呢,先看下面這段代碼。
  • Linux 拷貝、剪切、粘貼等常用命令詳解
    Linux 拷貝、剪切、粘貼等常用命令詳解複製粘貼命令:在一行的任何位置按下yy,y是yanked拷貝的意思,然後去想粘貼的位置按下p即可。p是粘貼的意思。如果想複製3行的話,按下3yy,就複製3行,如果想複製多行的話,直接按數字可以選中多好,然後粘貼。
  • Python 程式設計師如何防止數據被修改?
    :淺拷貝、深拷貝經過前2部分的解讀,大家對對象的引用賦值應該有了一個清晰的認識了。這個問題Python已經幫我們解決了,使用對象的拷貝或者深拷貝就可以愉快解決了。下面具體來看看Python中的淺拷貝與深拷貝是如何實現的。淺拷貝為了解決函數傳遞後被修改的問題,就需要拷貝一份副本,將副本傳遞給函數使用,就算是副本被修改,也不會影響原始數據 。
  • Python 標準庫之 sys & copy
    在諸多的 sys 函數和屬性中,我選幾個常用的來說明:1.sys.argvsys.argv 是專門用來向 Python 解釋器傳遞參數的,所以稱之為「命令行參數」,下面先解釋一下什麼是命令行參數:$ python3 --versionPython 3.6.5這裡的 --version 就是命令行參數,如果使用
  • 什麼是拷貝紙?拷貝紙的介紹
    拷貝紙是一種生產難度大的高檔文化工業用紙。該產品的技術特點是:物理強度高、均勻性、透明性、外觀性能都比較好,紙張表面細膩、平整、光滑、無氣泡砂、具有良好的印刷性能。一般來說,印刷紙的生產分為兩個基本過程:製漿和造紙。製漿是通過機械、化學或兩者聯合方法將植物纖維分離成天然紙漿或漂白紙漿。
  • linux-遠程拷貝文件之scp
    舉報   簡介   scp就是secure copy,是用來進行遠程文件拷貝的
  • 拷貝數變異才是病因
    曾經確信,人類基因組上0.1%的不同就足以形成各人種間的千差萬別,但2004年美國冷泉港實驗室和瑞士洛桑大學的兩個研究小組在《自然》和《科學》幾乎同時發表論文,報導了人體內拷貝數變異現象的存在。此後,其他研究小組的跟進研究進一步確認了拷貝數變異的存在。這一變異讓科學家認為兩個人之間,DNA代碼的不同之處可達到10%。
  • 中日韓人種基因拷貝數變異圖譜出爐
    據韓聯社4月5日報導,韓國首爾大學基因醫學研究所徐廷瑄教授領導的研究小組宣稱,他們通過對30名中國人、韓國人和日本人的基因組研究,成功繪製出中日韓人種超高清基因拷貝數變異圖譜,並根據該圖譜發現,亞洲人獨有的基因拷貝數變異共有3500多個。
  • 核酸分子量、拷貝數計算方法
    1A260 吸光度值=ds DNA50mg/ml=ss DNA33mg/ml=ss RNA40mg/ml(OD260) x (dilution factor) x [33 或40或50]/ (1000) = mg/mlMW = 克/摩爾 1 摩爾= 6.02 x 1023 摩爾分子(拷貝數)平均分子量
  • 人類神經元的馬賽克拷貝數變異性
    過去十年對神經元基因組的研究發現,例如,存在額外的或缺失的染色體或DNA片段可以通過這些基因組複製和複製。要想確定同一個體中的神經元攜帶獨特的DNA,唯一的方法就是對其單個細胞的基因組進行剖析,而不是細胞群的基因組,這是一個平均值。
  • 高齡孕婦胎兒基因組拷貝數變異發生率研究_高齡孕婦_拷貝數變異_高...
    結論    對進行有創產前診斷的高齡孕婦,在排除了常見的非整倍體異常後也應建議進行CNV檢測。關鍵詞:高齡孕婦;拷貝數變異;高通量測序;產前診斷我國《母嬰保健法實施辦法》明確指出,孕婦預產期年齡超過35周歲(高齡孕婦)是產前診斷的適應證。
  • 核糖體RNA基因拷貝數變異和表達調控方面獲進展
    真核生物的核糖體,主要由4種核糖體 RNA(rRNA)和80多種核糖體蛋白組成。其中,45S rRNA基因位點通過轉錄加工可以產生18S、5.8S和25S rRNA;而5SrRNA基因位點行使5S rRNA的轉錄。隨後,25S、5.8S以及5S RNA結合核糖體蛋白形成核糖體大亞基,同時18S RNA與其他核糖體蛋白形成核糖體小亞基,最終組裝成細胞中的「蛋白加工工廠」。
  • TiM:rDNA拷貝數的種內變化影響微生物群落分析嗎?
    在這裡,我們強調核糖體RNA(rRNA)基因拷貝數的種間和種內變異(圖1)是如何幹擾微生物群落組成分析的,特別是當重點關注真核微生物時,這些微生物的基因組在rRNA基因拷貝數上可以顯示出廣泛的種間和種內變異。
  • 研究發現沉默轉基因的激活不依賴於拷貝數和DNA過甲基化的變化
    以往的研究表明,許多因子都與基因沉默相關,如轉基因在基因組的插入位置、轉基因拷貝數目、重複序列、轉基因純合雜合狀態和DNA甲基化等。雖然定點疊加技術可以確保轉基因精準地插在有利於基因表達的基因組位置上,進而減少基因沉默的可能性,但也無法完全避免轉基因發生沉默。在區永祥課題組之前獲得的材料中,就有一個成功進行了基因定點疊加但轉基因出現了沉默現象的菸草株系(23.C.7)。
  • 拷貝數的巨大差異,黏粒DNA的得率比常規的高拷貝數質粒要低得多
    欲了解更多詳細信息和文獻引用,見第16章。黏粒資源一些公司 (例如Stratagene/Agilent公司和Epicentre Technologies 公司)以試劑盒形式銷售黏粒載體和包裝混合物以及細菌菌株。與質粒DNA轉化大腸桿菌(見方案1~4)相同的技術可用於轉化黏粒。
  • 鑲嵌拷貝數變異可增加自閉症風險
    鑲嵌拷貝數變異可增加自閉症風險 作者:小柯機器人 發布時間:2021/1/14 14:42:23 美國哈佛大學Po-Ru Loh等研究人員合作發現,鑲嵌拷貝數變異可增加自閉症風險。