可變對象與不可變對象 要理解深拷貝和淺拷貝,首先要理解可變對象和不可變對象。 不可變對象:該對象所指向的記憶體中的值不能被改變,修改對象的值時,由於其指向的值不能被改變,因此實際上是在記憶體中重新開闢一個地址用來存儲新的值,然後將對象指向這個新值。本質上是兩個對象,賦值前後對象id發生了變化。pytho ...
可變對象與不可變對象
要理解深拷貝和淺拷貝,首先要理解可變對象和不可變對象。
不可變對象:該對象所指向的記憶體中的值不能被改變,修改對象的值時,由於其指向的值不能被改變,因此實際上是在記憶體中重新開闢一個地址用來存儲新的值,然後將對象指向這個新值。本質上是兩個對象,賦值前後對象id發生了變化。python中的不可變對象包括:bool、int、str、float、tuple、frozenset、None。
可變對象:該對象所指向的記憶體中的值可以被改變。變數(引用)的值發生改變時,實際上是其指向的值直接發生改變,沒有開闢新的記憶體地址。python中的可變對象包括:list、dict、set。
python中的賦值語句不會創建對象的拷貝,僅僅只是將變數名稱綁定到一個對象上。對於不可變對象,這種操作不會產生差別,但是處理可變對象或可變對象的集合時,你可能希望創建這些對象的“真實拷貝”,在修改創建的拷貝時不改變原始的對象。
淺拷貝:通常指構造一個新的集合對象,然後用原始對象中的找到的子對象的引用來填充它。淺層的複製只有一層深度,複製過程中不會遞歸,所以不會創建子對象本身的副本。
深拷貝:深拷貝使複製過程遞歸,即首先構造一個新的集合對象,然後遞歸地用在原始對象中找到的子對象的副本來填充它。通過深拷貝複製對象,是原始對象及其所有子對象的完全獨立的克隆。
賦值與引用
python的賦值語句不會複製對象,而是創建一個對象的引用(可以理解為標簽)。代碼示例:
上圖示例中,創建了兩個變數(實際兩個變數表示的是同一個列表),但兩個變數id相同,指向的是同一個記憶體地址。
創建淺拷貝
仍以python列表為例,通常我們會用list()函數來複制一個列表,這個複製過程,就是一個淺拷貝。代碼示例:
可以看到,通過淺拷貝方式,確實是複製了一個列表。複製前後兩個變數的id不同,兩個變數指向兩個不同的記憶體地址,且修改其中一個列表中的值,對另一個列表不會產生影響。
而之所以稱這種複製方式為淺拷貝,是因為這種拷貝只對一層對象有效,當列表中有子對象時,對子對象的修改將同時影響原始對象和拷貝對象。代碼示例:
如上圖所示,修改第一層次的成員值,不會影響拷貝對象;修改子對象的成員值(第二層次),會同時影響原始對象和拷貝對象。這是因為淺拷貝沒有遞歸複製原始對象的值,只複製了第一層,因此拷貝對象中複製了子對象的引用,並沒有複製子對象的值。
創建深拷貝
python標準庫中的copy模塊提供了創建python對象的淺拷貝和深拷貝的介面。使用deepcopy()函數,可以創建一個對象的深拷貝。代碼示例:
如上圖所示,通過深拷貝複製的對象遞歸克隆了原始對象,兩者是完全獨立的。無論怎樣修改其中一個對象,都不會對另一個對象產生影響。
總結
- 不可變對象沒有深拷貝和淺拷貝之分,可以理解為都是深拷貝
- 創建對象的淺拷貝不會克隆子對象,不能完全對立與原始對象
- 深拷貝會遞歸克隆原始對象,兩者完全獨立,互不影響,創建深拷貝的速度較慢