1. 程式人生 > >Python中copy,deepcopy,=之深拷貝淺拷貝使用詳解

Python中copy,deepcopy,=之深拷貝淺拷貝使用詳解

    python中對於物件的拷貝分為淺拷貝(copy)和深拷貝(deepcopy)兩種方式。其中淺拷貝由“=”完成。而深拷貝由copy模組中deepcopy()函式擔任。

    淺拷貝和深拷貝的區別是:淺拷貝只是將原物件在記憶體中引用地址拷貝過來了。讓新的物件指向這個地址。而深拷貝是將這個物件的所有內容遍歷拷貝過來了,相當於跟原來沒關係了,所以如果你這時候修改原來物件的值跟他沒關係了,不會隨之更改。

1.淺拷貝"="的使用

#1.使用=複製不可變物件的值,以及複製以後修改其值後的變化。
val1 = 1000
val2 = val1
print("val1 is :{0},val2 is :{1}".format(val1,val2))#val1 is :1000,val2 is :1000
print(id(val1),id(val2))  #34052192 34052192
#這時候修改val1的值,儘管val2指向val1.但因為val1是不可變型別,修改其值,會重新給新值分配記憶體,然後指向他。
val1 += 1
print(val1,id(val1),val2,id(val2)) #1001 10131616 1000 10131568  值不一樣,記憶體地址也不一樣了

#1.使用=複製可變物件的值,以及複製以後修改其值後的變化。
ls1 =[1,2,3,4]
ls2 = ls1
print(id(ls1),id(ls2)) #43702792 43702792 直接使用=複製變數,記憶體地址一樣,值也一樣。
print(ls1,ls2) #[1, 2, 3, 4] [1, 2, 3, 4]直接使用=複製變數,記憶體地址一樣,值也一樣。
#這時候修改可變對的值,因為其值可變,所以只需要在原記憶體地址上修改即可。
ls1.append(5)
print(id(ls1),id(ls2)) #可變物件修改其值,記憶體引用不變
print(ls1,ls2) #[1, 2, 3, 4, 5] [1, 2, 3, 4, 5] 因為兩個變數的記憶體指向一樣,所以值也一樣。

2.深拷貝:copy.deepcopy()函式

#1.使用copy.deepcopy()拷貝不可變物件的值,以及複製以後修改其值後的變化。
val1 = 1000
val2 = copy.deepcopy(val1)
print("val1 is :{0},val2 is :{1}".format(val1,val2))#val1 is :1000,val2 is :1000
print(id(val1),id(val2))  #33717408 33717408 對於不可變物件,深度拷貝記憶體地址沒有修改。

val1 += 1
print(val1,id(val1),val2,id(val2)) #1001 33717904 1000 33717408

#1.使用copy.deepcopy()複製可變物件的值,以及複製以後修改其值後的變化。
ls1 =[1,2,3,4]
ls2 = copy.deepcopy(ls1)
print(id(ls1),id(ls2)) #34628472 34628712 注意對於可變物件深度拷貝後記憶體地址都修改了。
print(ls1,ls2) #[1, 2, 3, 4] [1, 2, 3, 4]
ls1.append(5)
print(id(ls1),id(ls2)) #34628472 34628712
print(ls1,ls2) #[1, 2, 3, 4, 5] [1, 2, 3, 4] #注意這個時候ls2的值沒有隨著ls1修改。

總結:其實對於淺拷貝和深拷貝來說,如果拷貝物件都是不可變物件的話,那麼兩者效果是一樣的。如果是可變物件的話,“=”拷貝的方式,只是拷貝了記憶體中的地址引用,兩個物件的地址引用一樣,所以兩個物件的值會隨著一方的修改而修改。而對於deepcopy()來說,如果是可變物件的話,那麼拷貝內容後新物件的記憶體地址也會重新分配,跟原來的記憶體地址不一樣了。所以兩者任意修改變數的內容不會對另一方造成影響。

3.注意一個特殊的copy(),跟深淺拷貝都有區別,慎用。

1.使用copy()拷貝不可變物件
val1 = 1000
val2 = copy.copy(val1)
print(val1,val2)##1000 1000
print(id(val1),id(val2))#8551568 8551568
2.使用copy()拷貝可變物件
ls1 =[1,2,3,4]
ls2 = copy.copy(ls1)
ls1.append(5)
print(ls1,ls2)  #[1, 2, 3, 4, 5] [1, 2, 3, 4]

看上去copy()函式效果和deepcopy()效果一樣,可變物件拷貝後值也沒有隨著一個物件的修改而修改。
然後真實情況真是這樣嘛?請看下面的案例,同樣是拷貝可變物件。

origin = [1, 2, [3, 4]]
cop1 = copy.copy(origin)
cop2 = copy.deepcopy(origin)
origin[2][0] = "hey!"  #修改資料來源的值
print(cop1,cop2) #[1, 2, ['hey!', 4]] [1, 2, [3, 4]]

很顯然這時copy()函式拷貝的值隨著原物件的值修改了,而deepcopy()的值沒有隨著原物件的值修改。
主要是因為deepcopy會將複雜物件的每一層複製一個單獨的個體出來對於copy()函式要慎用,慎用。