賦值,淺拷貝,深拷貝
一、賦值
str例
a=‘hello‘ b=‘hello‘ c=a print(id(a),id(b),id(c)) #2518845789440 2518845789440 2518845789440
我們可以發現a,b,c三者的地址是一樣的。所以以上賦值的操作就相當於c=a=b=‘hello‘
賦值是系統先給一個變量或者對象分配了內存,然後再將地址賦給a,b,c。所以它們的地址是相同的
list例
a=[‘hello‘] b=[‘hello‘] c=a print(id(a),id(b),id(c)) #1924016299912 1924016299976 1924016299912
但是這種情況卻不一樣了,a和b的地址不同,為什麽
因為str是不可變的,所以同樣是‘hello’只有一個地址,但是list是可變的,所以必須分配兩個地址
如果以上兩種情況修改了值會怎麽樣
str例
a=‘hello‘ b=‘hello‘ c=a a=‘world‘ print(id(a),id(b),id(c)) #2570701091368 2570703634360 2570703634360
這時a的地址和值變量,但是b,c地址和值都未變。因為str的不可變性,a要重新復制則需要重新開辟內存空間,所以a的值改變,a指向的地址改變,b,c由於‘hello’的不可變性,不會發生改變
list例
a=[‘hello‘] b=[‘hello‘] c=a a[0]=‘world‘ print(id(a),id(b),id(c)) #2476863774600 2476863774664 2476863774600
這時a,c的值和地址均改變,但二者仍相同,b不改變,由於list的可變性,所以修改list的值不需要另外開辟空間,只需修改原地址的值,所以a,c均改變
二、淺拷貝
1 a=[‘hello‘,[123,234]] 2 b=a[:] 3 print(id(a),id(b)) 4 print(id(a[0]),id(a[1])) 5print(id(b[0]),id(b[1])) 6 7 #2142142193608 2142142197896 8 #2142022964480 2142142193544 9 #2142022964480 2142142193544
我們可以看出a,b地址不同,這符合list是可變的,應開辟不同的空間,我們看第四行和第五行,發現元素的地址也是相同的。如果說字符串‘hello’地址一致還能理解,但是第二個元素的list地址仍一致。這就說明了淺拷貝的特點,只是將容器內的元素的地址復制了一份
接著我們嘗試修改a,b中的值
a=[‘hello‘,[123,234]] b=a[:] a[0]=‘world‘ a[1].append(345) print(a) print(b) #[‘world‘, [123, 234, 345]] #[‘hello‘, [123, 234, 345]]
a中第一個元素str改變,但是b中未改變,a中第二個元素改變,b中也改變,這就符合不可變的對象修改會開辟新的空間,可變的對象修改不會開辟新空間,也進一步證明了淺拷貝僅僅是復制了容器中元素的地址
三、深拷貝
from copy import deepcopy a=[‘hello‘,[123,234]] b=deepcopy(a) print(id(a),id(b)) print(id(a[0]),id(a[1])) print(id(b[0]),id(b[1]))
#2576042483656 2576042487944
#2575924887480 2576042483592
#2575924887480 2576042488136
深拷貝後,可以發現a,b地址以及a,b中元素地址均不同。這才是完全拷貝了一個副本
1 from copy import deepcopy 2 a=[‘hello‘,[123,234]] 3 b=deepcopy(a) 4 5 a[0]=‘world‘ 6 a[1].append(345) 7 print(a) 8 print(b)
#[‘world‘, [123, 234, 345]]
#[‘hello‘, [123, 234]]
從打印結果來看僅僅a修改了,b沒有任何修改。因為b是一個完全的副本,元素地址均與a不同,a修改,b不受影響
總結
1、賦值是將一個對象的地址賦值給一個變量,讓變量指向改地址
2、淺拷貝是在另一塊地址中創建一個新的變量或容器,但是容器內的元素的地址均是原對象的元素的地址的拷貝,也就是說新的容器中指向了舊的元素
3、深拷貝是在另一塊地址中創建一個新的變量或容器,同時容器內的元素的地址也是新開辟的,僅僅是值相同而已,是完全的副本
賦值,淺拷貝,深拷貝