參考連接: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
「=」的做用是引用原對象的在內存中的地址,讓新對象指向這個地址。指針
對於不可變對象,其值自己不容許被修改,數值的修改其實是讓變量指向了一個新的對象(新建立的對象)。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
對於可變對象類型,"="會引用對象地址,對其進行修改,只會改變對象值的內容,並不修改該對象(不建立新對象,不更改指針指向的地址)。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
效果同"="中的不可變對象
對於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,遞歸地將組合對象內部的對象進行深層引用。
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~