參考鏈接:
1. 介紹python中的可變類型與不可變類型:https://blog.csdn.net/answer3lin/article/details/86430074
(也可以參考轉載博客 Python中的不可變對象類型與可變對象類型)
2. 介紹等號賦值、copy、deepcopy的區別:https://blog.csdn.net/qq_26442553/article/details/82218403
建議讀者首先明白python中變量的本質、可變對象類型與不可變對象類型的區別,之后對於深淺拷貝會容易理解。
1. 等號"="
“=”的作用是引用原對象的在內存中的地址,讓新對象指向這個地址。
1.1 不可變對象
對於不可變對象,其值本身不允許被修改,數值的修改實際上是讓變量指向了一個新的對象(新創建的對象)。
圖1. 不可變對象類型"="拷貝
1 # 1.使用=復制不可變對象的值,以及復制以后修改其值后的變化。 2 val1 = 1000 3 val2 = val1 4 print("val1 is :{0},val2 is :{1}".format(val1,val2)) 5 # val1 is :1000,val2 is :1000 6 print(id(val1),id(val2)) 7 # 139692380387760 139692380387760 8 9 # 修改val1的值,因為val1是不可變類型,修改其值,會重新給新值分配內存,然后指向他。 10 val1 += 1 11 print(val1,id(val1),val2,id(val2)) 12 # 1001 139692380387216 1000 139692380387760
1.2 可變對象類型
對於可變對象類型,"="會引用對象地址,對其進行修改,只會改變對象值的內容,並不修改該對象(不創建新對象,不更改指針指向的地址)。
Tips:
list對象的地址和list[0]對象的地址是不同的。可以理解為list這個組合對象中插入的是其中各個對象的地址的引用。
圖2. list對象
1 #1.使用=復制可變對象的值,以及復制以后修改其值后的變化。 2 ls1 =[1,2,3,4] 3 ls2 = ls1 4 print(id(ls1),id(ls2)) 5 # 140520429283400 140520429283400 6 # 直接使用=復制變量,內存地址一樣,值也一樣。 7 print(ls1,ls2) 8 #[1, 2, 3, 4] [1, 2, 3, 4] 9 #直接使用=復制變量,內存地址一樣,值也一樣。 10 #這時候修改可變對的值,因為其值可變,所以只需要在原內存地址上修改即可。 11 ls1.append(5) 12 print(id(ls1),id(ls2)) 13 # 140520429283400 140520429283400 14 #可變對象修改其值,內存引用不變 15 print(ls1,ls2) 16 #[1, 2, 3, 4, 5] [1, 2, 3, 4, 5] 因為兩個變量的內存指向一樣,所以值也一樣。 17 18 print(id(ls1), id(ls2)) 19 print(id(ls1[0]), id(ls2[0])) 20 print(id(ls1[1]), id(ls2[1])) 21 print(id(ls1[2]), id(ls2[2])) 22 # 140520429283400 140520429283400 23 # 94472536902176 94472536902176 24 # 94472536902208 94472536902208 25 # 94472536902240 94472536902240
2. copy
2.1 不可變對象類型
效果同"="中的不可變對象
2.2 可變對象類型
對於copy一個可變對象類型的對象,會重新創建一個新的對象,如果原對象是一個組合對象(比如list, 類實例等),會將原組合對象中的各個對象的引用插入到新創建的對象中。
注意,copy只是將其第一層組成對象的內存地址引用過來,並不迭代的引用組成對象的組成對象的地址!所以,如果其組成對象是可變類型,雖然引用地址不變,但其內容可能會變化。詳見下面兩個例子:
例1.
1 import copy 2 ls1 =[1,2,3,4] 3 ls2 = copy.copy(ls1) 4 print(id(ls1), id(ls2)) 5 # 140616587448072 140616587497224 6 7 ls1.append(5) 8 print(ls1,ls2) 9 #[1, 2, 3, 4, 5] [1, 2, 3, 4] 10 11 print(id(ls1), id(ls2)) 12 print(id(ls1[0]), id(ls2[0])) 13 # 140616587448072 140616587497224 14 # 94889387870752 94889387870752
例2.
1 origin = [1, 2, [3, 4]] 2 cop1 = copy.copy(origin) 3 origin[2][0] = "hey!" #修改數據源的值 4 print(cop1) 5 #[1, 2, ['hey!', 4]]
對於例2的解釋
圖3. origin = [1, 2, [3, 4]]的內部結構 圖4. copy的實現
可以看出copy只在新對象中插入了第一層的地址引用. 其中只有有橘色框的對象是新建的對象。
那么如果想要拷貝前后兩個對象完全互相獨立,互不影響要怎樣做呢?用deepcopy,遞歸地將組合對象內部的對象進行深層引用。
3. deepcopy
copy與deepcopy的區別:
The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
1 origin = [1, 2, [3, 4]] 2 cop2 = copy.deepcopy(origin) 3 origin[2][0] = "hey!" #修改數據源的值 4 print(cop2) # [1, 2, [3, 4]]
對比2.2的例2會發現deepcopy遞歸的將組合對象的每一層的對象都進行了復制。因此,original的對象與deep copy之后的對象是內存地址完全不同的,完全獨立的。
圖3. origin = [1, 2, [3, 4]]的內部結構 圖4. copy的實現 圖5. deepcopy與copy實現的對比
其中橘色是deepcopy實現,不僅新建了第一層addr9處的對象,也遞歸地新建了addr10處的對象,並將addr10引用插入到addr9處的對象中;遞歸到指向不可變類型的對象為止;因而原對象與新對象是完全獨立,互不影響的。
其中綠色是copy實現,僅新建了第一層addr8處的對象,直接將addr5處對象的引用插入到addr8中,並沒有為其重新開辟空間,新建對象,因而,原對象中更深層次中內容的變換,會直接影響新對象內容,二者並非完全獨立。
所以,慎用copy~