淺拷貝
copy.copy()
copy函數(shù)是淺拷貝嘉冒,只對(duì)可變類型的第一層對(duì)象進(jìn)行拷貝弊予,對(duì)拷貝的對(duì)象開辟新的內(nèi)存空間進(jìn)行存儲(chǔ)丐枉,不會(huì)拷貝對(duì)象內(nèi)部的子對(duì)象
特性: 淺拷貝只會(huì)對(duì)可變類型的第一層進(jìn)行拷貝溪食;
- 可變類型:只能作用于列表/字典/集合,而不能拷貝數(shù)字/字符串/元組力九;
- 第一層:例如一個(gè)列表內(nèi)的嵌套列表無(wú)法拷貝[1,2,[a,b],3]。
如何驗(yàn)證?
通過(guò)id()
對(duì)比拷貝前后對(duì)象的內(nèi)存地址塞赂,或直接用is
方法判斷拷貝前后對(duì)象內(nèi)存地址是否相同泪勒。
驗(yàn)證示例:
-
字符串淺拷貝
import copy a = "abc" b = copy.copy(a) print("a的內(nèi)存地址:",id(a)) print("b的內(nèi)存地址:",id(b))
輸出:
a的內(nèi)存地址: 2140603563288 b的內(nèi)存地址: 2140603563288
結(jié)論:未拷貝
-
數(shù)值淺拷貝
a = 12 b = copy.copy(a) print("a的內(nèi)存地址:",id(a)) print("b的內(nèi)存地址:",id(b))
輸出:
a的內(nèi)存地址: 1896907216 b的內(nèi)存地址: 1896907216
結(jié)論:未拷貝
-
列表淺拷貝
a = [1,2,3] b = copy.copy(a) print("a的內(nèi)存地址:",id(a)) print("b的內(nèi)存地址:",id(b))
輸出:
a的內(nèi)存地址: 2140688273224 b的內(nèi)存地址: 2140681134216
結(jié)論:拷貝成功,創(chuàng)建了新的內(nèi)存地址
-
列表嵌套情況淺拷貝(特別注意)
a = [1,2,[3,4]] b = copy.copy(a) print("a:",a) print("a的內(nèi)存地址:",id(a)) print("b:",b) print("b的內(nèi)存地址:",id(b))
輸出:
a: [1, 2, [3, 4]] a的內(nèi)存地址: 2140679959240 b: [1, 2, [3, 4]] b的內(nèi)存地址: 2140679959112
外部地址改變,創(chuàng)建了新的內(nèi)存地址
驗(yàn)證內(nèi)部嵌套列表是否拷貝:
In [43]: id(a[2]) Out[43]: 2140680066952 In [44]: id(b[2]) Out[44]: 2140680066952
結(jié)論:嵌套列表內(nèi)存地址相同酣藻,未進(jìn)行拷貝
再次驗(yàn)證曹洽,嘗試修改a列表鳍置,是否對(duì)b列表造成影響:
In [45]: a.append(5) In [46]: a[2].append("a") In [48]: b Out[48]: [1, 2, [3, 4, 'a']]
分析:a列表修改外層列表不影響b列表辽剧;修改a列表的子列表會(huì)同時(shí)改變b列表的子列表。
結(jié)論:
符合淺拷貝的特性税产,淺拷貝只會(huì)對(duì)可變類型的第一層進(jìn)行拷貝怕轿,而不會(huì)拷貝其可變類型的子對(duì)象,因此未拷貝部分指向的仍是同一個(gè)內(nèi)存地址辟拷;
深拷貝
copy.deepcopy
deepcopy函數(shù)是深拷貝, 只要發(fā)現(xiàn)對(duì)象有可變類型就會(huì)對(duì)該對(duì)象到最后一個(gè)可變類型的每一層對(duì)象就行拷貝, 對(duì)每一層拷貝的對(duì)象都會(huì)開辟新的內(nèi)存空間進(jìn)行存儲(chǔ)撞羽。
深拷貝同樣無(wú)法拷貝不可變類型:字符串、數(shù)字衫冻、元組诀紊。
- 不可變類型的深拷貝(主要討論元組及其子元素)
# 不可變類型元組(需要特別注意)
In [59]: a = (1,2)
In [60]: b = copy.deepcopy(a)
In [61]: id(a)
Out[61]: 2140688276424
In [62]: id(b)
Out[62]: 2140688276424
In [63]: a = (1,2,[1,3])
In [64]: b =copy.deepcopy(a)
# 此處雖然外層元組是不可變類型,但內(nèi)存地址依然改變了隅俘,原因是深拷貝會(huì)對(duì)所有可變子對(duì)象進(jìn)行拷貝邻奠,因此內(nèi)部列表會(huì)被拷貝,內(nèi)存地址改變
In [65]: id(a)
Out[65]: 2140685431720
In [66]: id(b)
Out[66]: 2140688106408
# 其內(nèi)部的子列表被拷貝为居,內(nèi)存地址改變碌宴,由于元組是不可變類型,內(nèi)部改變蒙畴,其本身地址也會(huì)改變贰镣。
In [67]: id(a[2])
Out[67]: 2140681195272
In [68]: id(b[2])
Out[68]: 2140687283336
# 其內(nèi)部的不可變對(duì)象無(wú)法拷貝,內(nèi)存地址不變
In [69]: id(a[1])
Out[69]: 1896906896
In [70]: id(b[1])
Out[70]: 1896906896
? 結(jié)論:
? 不可變類型進(jìn)行深拷貝如果子對(duì)象沒(méi)有可變類型則不會(huì)進(jìn)行拷貝膳凝,而只 是拷貝了這個(gè)對(duì)象的引用碑隆,否則會(huì)對(duì)該對(duì)象到最后一個(gè)可變類型的每一層 對(duì)象就行拷貝, 對(duì)每一層拷貝的對(duì)象都會(huì)開辟新的內(nèi)存空間進(jìn)行存儲(chǔ)
-
可變類型的深拷貝(主要討論列表及子列表)
In [77]: a = [1,2,[1,2,3]] In [78]: b = copy.deepcopy(a) # 外部列表內(nèi)存地址改變 In [79]: id(a) Out[79]: 2140687234824 In [80]: id(b) Out[80]: 2140681187208 # 子列表內(nèi)存地址也改變 In [81]: id(a[2]) Out[81]: 2140681025608 In [82]: id(b[2]) Out[82]: 2140690147272 # 改變子列表元素不會(huì)再影響deepcoy的子列表元素 In [83]: a[2].append(3) In [84]: a Out[84]: [1, 2, [1, 2, 3, 3]] In [85]: b Out[85]: [1, 2, [1, 2, 3]]
結(jié)論:
可變類型進(jìn)行深拷貝會(huì)對(duì)該對(duì)象到最后一個(gè)可變類型的每一層對(duì)象就行拷貝, 對(duì)每一層拷貝的對(duì)象都會(huì)開辟新的內(nèi)存空間進(jìn)行存儲(chǔ)。
總結(jié)
- 淺拷貝使用copy.copy函數(shù)
- 深拷貝使用copy.deepcopy函數(shù)
- 不管是給對(duì)象進(jìn)行深拷貝還是淺拷貝蹬音,只要拷貝成功就會(huì)開辟新的內(nèi)存空間存儲(chǔ)拷貝的對(duì)象上煤。
- 淺拷貝和深拷貝的區(qū)別是:
- 淺拷貝最多拷貝對(duì)象的一層,深拷貝可能拷貝對(duì)象的多層祟绊。