Python 中淺拷貝和深拷貝的區別


     
     
     

點擊上方AI算法與圖像處理”,選擇加"星標"或“置頂”

重磅乾貨,第一時間送達


CVPR 2021 相關論文、代碼 、解讀和demo整理,同時爲了方便下載論文,已把部分論文上傳到上面了,歡迎小夥伴們 star 支持一波!

https://github.com/DWCTOD/CVPR2021-Papers-with-Code-Demo


引言


Python 附帶了一個名爲 copy 的模塊,它提供了特定的複製功能。在本文中,我們將探索什麼是深拷貝和淺拷貝。此外,我們還將討論它們之間的差異以及何時使用其中一種而不是另一種。


不可變對象 vs 可變對象


在進入 Python 中的淺拷貝和深拷貝之前,首先要理解可變對象類型和不可變對象類型之間的區別。顧名思義,不可變對象是不可以被修改的,因此,當這些對象的值被修改時,Python 會創建一個新的對象。


例如,假設我們有兩個變量引用同一個整數對象:

>>> a = 10>>> b = a  # variables a and b hold the reference to the same object

現在,如果我們對變量 a 執行任何類型的操作,並且考慮到 Python 中的整數是不可變的,那麼結果將會創建一個保存新值的新對象。這意味着對象的舊值(以及引用它的所有變量)將保持不變:

>>> a = a + 1>>> print(a)11>>> print(b)10

另一方面,可變對象類型允許對對象值進行就地修改。這意味着,當修改可變對象類型的值時,保存對同一對象的引用的所有變量都會受到影響。例如,假設我們確實有以下列表

>>> list_1 = [1, 2, 3]>>> list_2 = list_1

考慮到 Python 中的列表是可變的,如果我們改變這兩個列表中的任何一個,這個操作也會對其他變量產生直接影響,因爲它們都指向內存中相同的對象引用。

>>> list_1[0] = 0>>> print(list_1)[0, 2, 3]>>> print(list_2)[0, 2, 3]


常規賦值


複製對象最直接的方法是通過常規的賦值操作。假設我們有一下操作:

a = [1, 2, 3]b = a

在這種情況下,變量 a 和 b 對同一個對象都有相同的引用。這意味着,如果這兩個變量中的任何一個用於執行就地修改,其他變量也將受到影響。

>>> a[0] = 0>>> print(a)[0, 2, 3]>>> print(b)[0, 2, 3]

因此,當我們必須處理不可變的對象類型時,通常會使用常規的賦值操作。在這種情況下,當使用兩個變量中的任何一個執行操作時,另一個變量將保持不變,因爲它的引用指向的是不變的舊對象。

>>> id(a) == id(b)True

Python 中的賦值語句不復制對象,它們在目標和對象之間創建綁定。


淺拷貝 vs 深拷貝


在深入討論淺拷貝和深拷貝的細節之前,請注意,它們的區別只有在我們必須處理本質上是嵌套結構的複合對象時纔有意義。換句話說,複合對象是包含其他對象的對象,例如,列表列表或集合字典。


一個淺拷貝將獲得一個原始對象的副本並創建一個新的複合對象,但是如果我們正在複製的對象是一個複合對象,那麼內部對象將與在原始對象中找到的對象相同。

>>> import copy>>> b = copy.copy(a)>>> id(a) == id(b)False

如我們所見,列表對象 a 和 b 是不同的,這意味着它們持有指向內存中不同對象的不同引用(即使這些對象的值相同)。


當我們需要處理複合對象時,事情會變得有點複雜。現在讓我們假設變量 a 是一個複合對象,它表示一個列表列表:

a = [[1, 2, 3], [4, 5, 6]]

現在讓我們對 a 進行淺拷貝:

>>> import copy>>> b = copy.copy(a)

我們可以看到 a 和 b 是不同的對象:

>>> id(a) == id(b)False

然而,內部對象(即兩個內部列表)與原始對象引用的對象相同:

>>> id(a[0]) == id(b[0])True

這是非常危險的,因爲任何內部列表的更改都會影響引用這些內部列表的其他複合對象:

>>> a[0][0] = 0>>> a[[0, 2, 3], [4, 5, 6]]>>> b[[0, 2, 3], [4, 5, 6]]

因此,只有當我們不必處理複合對象時,淺拷貝才適用。


淺拷貝構造一個新的複合對象,然後(在可能的範圍內)將對原始對象中找到的對象的引用插入其中。


深層拷貝將獲取原始對象的副本,然後遞歸地獲取找到的內部對象的副本(如果有的話)。

>>> import copy>>> a = [[1, 2, 3], [4, 5, 6]]>>> b = copy.deepcopy(a)

同樣,我們可以看到原始對象和複製對象在本質上是不同的:

>>> id(a) == id(b)False

但在這種情況下,即使是內部對象也會不同:

>>> id(a[0]) == id(b[0])False

這意味着 a 中任何嵌套列表的更改都不會影響對象 b 中的相應列表:

>>> a[0][0] = 0>>> a[[0, 2, 3], [4, 5, 6]]>>> b[[1, 2, 3], [4, 5, 6]]

因此,當我們必須處理複合對象並希望確保任何內部對象的更改都不會影響引用相同對象的其他變量時,深拷貝更爲合適。


深拷貝構造一個新的複合對象,然後遞歸地將原始對象中找到的對象的副本插入其中。


總結


在本文中,我們探討了用 Python 複製對象的三種基本方法。最初,我們討論了不可變對象類型和可變對象類型之間的區別。不需要複製不可變物件類型,因爲這些實例的值永遠不會改變。另一方面,開發人員在修改可變對象類型時需要非常小心,因爲這個操作可能會潛在地影響保存相同對象的引用的其他變量。當此類對象就地更改時,引用同一對象的所有其他變量也將受到此更改的影響。


因此,瞭解如何正確地複製可變對象以避免代碼中的 bug 非常重要。回想一下,一個淺拷貝將從原始對象中創建一個新對象,但是如果對象包含其他對象,那麼內部對象將不會被複制。另一方面,深度拷貝將爲複合對象中包含的內部對象創建一個新對象。


·  END  ·


HAPPY LIFE

  
       
       
       

個人微信(如果沒有備註不拉羣!
請註明: 地區+學校/企業+研究方向+暱稱



下載1:何愷明頂會分享


AI算法與圖像處理」公衆號後臺回覆:何愷明,即可下載。總共有6份PDF,涉及 ResNet、Mask RCNN等經典工作的總結分析


下載2:終身受益的編程指南:Google編程風格指南


AI算法與圖像處理」公衆號後臺回覆:c++,即可下載。歷經十年考驗,最權威的編程規範!




     
     
     
下載3 CVPR2021

AI算法與圖像處公衆號後臺回覆: CVPR 即可下載1467篇CVPR 2020論文 和 CVPR 2021 最新論文

點亮 ,告訴大家你也在看



本文分享自微信公衆號 - AI算法與圖像處理(AI_study)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章