前面介紹了Python的賦值(對(duì)象的引用傳遞),那么Python如何解決原始數(shù)據(jù)在函數(shù)傳遞后不受影響呢,Python提供了淺度拷貝(shallowcopy)和深度拷貝(deepcopy)兩種方式。
·淺拷貝(copy):拷貝父對(duì)象,不拷貝對(duì)象內(nèi)部的子對(duì)象。
·深拷貝(deepcopy):完全拷貝了父對(duì)象及其子對(duì)象。
淺拷貝
1.不可變數(shù)據(jù)類型
下面對(duì)不可變對(duì)象整型變量和元組進(jìn)行淺拷貝:
importcopy
a=1
b=copy.copy(a)
print(id(a))
print(id(b))
print(a==b)
print(aisb)
t1=(1,2,3)
t2=tuple(t1)
print(id(t1))
print(id(t2))
print(t1==t2)
print(t1ist2)
執(zhí)行結(jié)果:
50622072
50622072
True
True
55145384
55145384
True
True
不可變對(duì)象的拷貝和對(duì)象的引用傳遞一樣,a、b指向相同的對(duì)象,修改其中一個(gè)變量的值不會(huì)影響另外的變量,會(huì)開(kāi)辟新的空間。
2.可變數(shù)據(jù)類型
對(duì)可變對(duì)象list進(jìn)行淺拷貝:
importcopy
l1=[1,2,3]
l2=list(l1)
l3=copy.copy(l1)
l4=l1[:]
print(id(l1))
print(id(l2))
print(l1==l2)
print(l1isl2)
print(id(l3))
print(id(l4))
l1.append(4)
print(id(l1))
print(l1==l2)
print(l1isl2)
執(zhí)行結(jié)果:
48520904
48523784
True
False
48523848
48521032
48520904
False
False
可以看到,對(duì)可變對(duì)象的淺拷貝會(huì)重新分配一塊內(nèi)存,創(chuàng)建一個(gè)新的對(duì)象,里面的元素是原對(duì)象中子對(duì)象的引用。改變l1的值不會(huì)影響l2,l3,l4的值,它們指向不同的對(duì)象。
上面的例子比較簡(jiǎn)單,下面舉一個(gè)相對(duì)復(fù)雜的數(shù)據(jù)結(jié)構(gòu):
importcopy
l1=[[1,2],(4,5)]
l2=copy.copy(l1)
print(id(l1))
print(id(l2))
print(id(l1[0]))
print(id(l2[0]))
l1.append(6)
print(l1)
print(l2)
l1[0].append(3)
print(l1)
print(l2)
執(zhí)行結(jié)果:
1918057951816
1918057949448
2680328991496
2680328991496
[[1,2],(4,5),6]
[[1,2],(4,5)]
[[1,2,3],(4,5),6]
[[1,2,3],(4,5)]
l2是l1的淺拷貝,它們指向不同的對(duì)象,因?yàn)闇\拷貝里的元素是對(duì)原對(duì)象元素的引用,因此l2中的元素和l1指向同一個(gè)列表和元組對(duì)象(l1[0]和l2[0]指向的是相同的地址)。l1.append(6)不會(huì)對(duì)l2產(chǎn)生任何影響,因?yàn)閘2和l1作為整體是兩個(gè)不同的對(duì)象,不共享內(nèi)存地址。
l1[0].append(3)對(duì)l1中的第一個(gè)列表新增元素3,因?yàn)閘2是l1的淺拷貝,l2中的第一個(gè)元素和l1中的第一個(gè)元素,共同指向同一個(gè)列表,因此l2中的第一個(gè)列表也會(huì)相對(duì)應(yīng)的新增元素3。
這里提一個(gè)小問(wèn)題:如果對(duì)l1中的元組新增元素(l1[1]+=(7,8)),會(huì)影響l2嗎?
到這里我們知道使用淺拷貝可能帶來(lái)的副作用,要避免它就得使用深度拷貝。
深度拷貝
深度拷貝會(huì)完整地拷貝一個(gè)對(duì)象,會(huì)重新分配一塊內(nèi)存,創(chuàng)建一個(gè)新的對(duì)象,并且將原對(duì)象中的元素以遞歸的方式,通過(guò)創(chuàng)建新的子對(duì)象拷貝到新對(duì)象中。因此,新對(duì)象和原對(duì)象沒(méi)有任何關(guān)聯(lián),也就是完全拷貝了父對(duì)象及其子對(duì)象。
importcopy
l1=[[1,2],(4,5)]
l2=copy.deepcopy(l1)
print(id(l1))
print(id(l2))
l1.append(6)
print(l1)
print(l2)
l1[0].append(3)
print(l1)
print(l2)
執(zhí)行結(jié)果:
3026088342280
3026088342472
[[1,2],(4,5),6]
[[1,2],(4,5)]
[[1,2,3],(4,5),6]
[[1,2],(4,5)]
可以看到,l1變化不影響l2,l1和l2完全獨(dú)立,沒(méi)有任何聯(lián)系。
在進(jìn)行深度拷貝時(shí),深度拷貝deepcopy中會(huì)維護(hù)一個(gè)字典,記錄已經(jīng)拷貝的對(duì)象與其ID。如果字典里已經(jīng)存儲(chǔ)了將要拷貝的對(duì)象,則會(huì)從字典直接返回。
以上內(nèi)容為大家介紹了Python淺拷貝和深度拷貝,希望對(duì)大家有所幫助,如果想要了解更多Python相關(guān)知識(shí),請(qǐng)關(guān)注IT培訓(xùn)機(jī)構(gòu):千鋒教育。http://m.2667701.com/