首先需要了解下幾個概念
- 變量:是一個系統表的元素,擁有指向對象的連接空間
- 對象:被分配的一塊內存,存儲其所代表的值
- 引用:是自動形成的從變量到對象的指針
- 類型:屬於對象,而非變量
- 不可變對象:一旦創建就不可修改的對象,包括字符串、元組、數值類型
(該對象所指向的內存中的值不能被改變。當改變某個變量時候,由於其所指的值不能被改變,相當於把原來的值複製一份後再改變,這會開闢一個新的地址,變量再指向這個新的地址。)
- 可變對象:可以修改的對象,包括列表、字典、集合
(該對象所指向的內存中的值可以被改變。變量(準確的說是引用)改變後,實際上是其所指的值直接發生改變,並沒有發生複製行爲,也沒有開闢新的地址,通俗點說就是原地改變。)
當我們寫:
a = 'python'
Python解釋器乾的事情:
1. 創建也變量a
2. 創建一個對象(分配一塊內存),來存儲值 'python'
3. 將變量與對象,通過指針連接起來,從變量到對象的連接稱之爲引用(變量引用對象)
1.賦值: 只是複製了新對象的引用,不會開闢新的內存空間。
並不會產生一個獨立的對象單獨存在,只是將原有的數據塊打上一個新標籤,所以當其中一個標籤被改變的時候,數據塊就會發生變化,另一個標籤也會隨之改變。
2.淺拷貝: 創建新對象,其內容是原對象的引用。
淺拷貝有三種形式: 切片操作,工廠函數,copy模塊中的copy函數。
如: lst = [1,2,[3,4]]
切片操作:lst1 = lst[:] 或者 lst1 = [each for each in lst]
工廠函數:lst1 = list(lst)
copy函數:lst1 = copy.copy(lst)
淺拷貝之所以稱爲淺拷貝,是它僅僅只拷貝了一層,拷貝了最外圍的對象本身,內部的元素都只是拷貝了一個引用而已,在lst中有一個嵌套的list[3,4],如果我們修改了它,情況就不一樣了。
淺複製要分兩種情況進行討論:
1)當淺複製的值是不可變對象(字符串、元組、數值類型)時和“賦值”的情況一樣,對象的id值(id()函數用於獲取對象的內存地址)與淺複製原來的值相同。
2)當淺複製的值是可變對象(列表、字典、集合)時會產生一個“不是那麼獨立的對象”存在。有兩種情況:
第一種情況:複製的對象中無複雜子對象,原來值的改變並不會影響淺複製的值,同時淺複製的值改變也並不會影響原來的值。原來值的id值與淺複製原來的值不同。
第二種情況:複製的對象中有複雜子對象(例如列表中的一個子元素是一個列表),如果不改變其中複雜子對象,淺複製的值改變並不會影響原來的值。 但是改變原來的值中的複雜子對象的值會影響淺複製的值。
3.深拷貝:和淺拷貝對應,深拷貝拷貝了對象的所有元素,包括多層嵌套的元素。深拷貝出來的對象是一個全新的對象,不再與原來的對象有任何關聯。
所以改變原有被複制對象不會對已經複製出來的新對象產生影響。
只有一種形式,copy模塊中的deepcopy函數
對於不可變對象的深淺拷貝
import copy
a=(1,2,3)
print("=====賦值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))
print("=====淺拷貝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))
print("=====深拷貝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))
結果:
=====賦值=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====淺拷貝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====深拷貝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
不可變對象類型,沒有被拷貝的說法,即便是用深拷貝,查看id的話也是一樣的,如果對其重新賦值,也只是新創建一個對象,替換掉舊的而已。
一句話就是,不可變類型,不管是深拷貝還是淺拷貝,地址值和拷貝後的值都是一樣的。
對於可變對象深淺拷貝
import copy
a=[1,2,3]
print("=====賦值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))
print("=====淺拷貝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))
print("=====深拷貝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))
結果:
=====賦值=====
[1, 2, 3]
[1, 2, 3]
37235144
37235144
=====淺拷貝=====
[1, 2, 3]
[1, 2, 3]
37235144
37191432
=====深拷貝=====
[1, 2, 3]
[1, 2, 3]
37235144
37210184
賦值: 值相等,地址相等
copy淺拷貝:值相等,地址不相等
deepcopy深拷貝:值相等,地址不相等
對於可變對象深淺拷貝(外層改變元素)
import copy
l=[1,2,3,[4, 5]]
l1=l #賦值
l2=copy.copy(l) #淺拷貝
l3=copy.deepcopy(l) #深拷貝
l.append(6)
print(l)
print(l1)
print(l2)
print(l3)
結果:
[1, 2, 3, [4, 5], 6] #l添加一個元素6
[1, 2, 3, [4, 5], 6] #l1跟着添加一個元素6
[1, 2, 3, [4, 5]] #l2保持不變
[1, 2, 3, [4, 5]] #l3保持不變
對於可變對象深淺拷貝(內層改變元素)
import copy
l=[1,2,3,[4, 5]]
l1=l #賦值
l2=copy.copy(l) #淺拷貝
l3=copy.deepcopy(l) #深拷貝
l[3].append(6)
print(l)
print(l1)
print(l2)
print(l3)
結果:
[1, 2, 3, [4, 5, 6]] #l[3]添加一個元素6
[1, 2, 3, [4, 5, 6]] #l1跟着添加一個元素6
[1, 2, 3, [4, 5, 6]] #l2跟着添加一個元素6
[1, 2, 3, [4, 5]] #l3保持不變
1.外層添加元素時,淺拷貝不會隨原列表變化而變化;內層添加元素時,淺拷貝纔會變化。
2.無論原列表如何變化,深拷貝都保持不變。
3.賦值對象隨着原列表一起變化。