賦值、淺拷貝和深拷貝的區別?
首先要知道變數,物件,引用三者之間的關係
變數: 是一個元素, 擁有指向物件的連線空間
**物件:**被分配的一塊記憶體,儲存代表的值
引用: 是變數到物件的指標
- 一、賦值
在 Python 中,物件的賦值就是簡單的物件引用, 如下所示:
a = [1,2,"hello",['python', 'C++']]
b = a
在上述情況下,a 和 b 是一樣的,他們指向同一片記憶體,b 不過是 a 的別名,是引用。
我們可以使用 b is a 去判斷,返回 True,表明他們地址相同,內容相同,也可以使用 id()函式來檢視兩個列表的地址是否相同。 賦值操作(包括物件作為引數、返回值)不會開闢新的記憶體空間,它只是複製了物件的引用。也就是說除了 b 這個名字之外,沒有其他的記憶體開銷。修改了 a,也就影響了 b,同理,修改了 b,也就影響了 a。
- 二、淺拷貝(shallow copy)
淺拷貝會建立新物件,其內容非原物件本身的引用,而是原物件內第一層物件的引用。
**淺拷貝有三種形式:**切片操作、工廠函式、copy 模組中的 copy 函式。
比如上述的列表 a; 切片操作:b = a[:] 或者 b = [x for x in a]; 工廠函式:b = list(a); copy 函式:b = copy.copy(a);
淺拷貝產生的列表 b 不再是列表 a 了,使用 is 判斷可以發現他們不是同一個物件,使用 id 檢視,他們也不指向同一片記憶體空間。但是當我們使用 id(x) for x in a 和 id(x) for x in b 來檢視 a 和 b 中元素的地址時,可以看到二者包含的元素的地址是相同的。在這種情況下,列表 a 和 b 是不同的物件,修改列表 b 理論上不會影響到列表 a。
但是要注意的是,淺拷貝之所以稱之為淺拷貝,是它僅僅只拷貝了一層,在列表 a 中有一個巢狀的list,如果我們修改了它,情況就不一樣了。 比如:a[3].append(‘java’)。檢視列表 b,會發現列表 b 也發生了變化,這是因為,我們修改了巢狀的 list,修改外層元素,會修改它的引用,讓它們指向別的位置,修改巢狀列表中的元素,列表的地址並未發生變化,指向的都是用一個位置。
- 三、深拷貝(deep copy)
深拷貝只有一種形式,copy 模組中的 deepcopy()函式。 深拷貝和淺拷貝對應,深拷貝拷貝了物件的所有元素,包括多層巢狀的元素。因此,它的時間和空間開銷要高。 同樣的對列表 a,如果使用 b = copy.deepcopy(a),再修改列表 b 將不會影響到列表 a,即使巢狀的列表具有更深的層次,也不會產生任何影響,因為深拷貝拷貝出來的物件根本就是一個全新的物件,不再與原來的物件有任何的關聯。
- 四、拷貝的注意點?
對於非容器型別,如數字、字元,以及其他的“原子”型別,沒有拷貝一說,產生的都是原物件的引用。如果元組變數值包含原子型別物件,即使採用了深拷貝,也只能得到淺拷貝。