[Python] 等號賦值, copy, deepcopy的區別

參考連接:html

1. 介紹python中的可變類型與不可變類型:https://blog.csdn.net/answer3lin/article/details/86430074python

(也能夠參考轉載博客 Python中的不可變對象類型與可變對象類型)app

2. 介紹等號賦值、copy、deepcopy的區別:https://blog.csdn.net/qq_26442553/article/details/82218403spa

建議讀者首先明白python中變量的本質、可變對象類型與不可變對象類型的區別,以後對於深淺拷貝會容易理解。.net


 1. 等號"="

「=」的做用是引用原對象的在內存中的地址,讓新對象指向這個地址。指針

1.1 不可變對象

對於不可變對象,其值自己不容許被修改數值的修改其實是讓變量指向了一個新的對象(新建立的對象)。code

圖1. 不可變對象類型"="拷貝orm

 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 可變對象類型

對於可變對象類型,"="會引用對象地址,對其進行修改,只會改變對象值的內容,並不修改該對象(不建立新對象,不更改指針指向的地址)。htm

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):

  • shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.

  • 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~

相關文章
相關標籤/搜索