淺拷貝
copy.copy()
copy函數是淺拷貝,只對可變類型的第一層對象進行拷貝,對拷貝的對象開辟新的內存空間進行存儲,不會拷貝對象內部的子對象
特性: 淺拷貝只會對可變類型的第一層進行拷貝;
- 可變類型:只能作用于列表/字典/集合,而不能拷貝數字/字符串/元組;
- 第一層:例如一個列表內的嵌套列表無法拷貝[1,2,[a,b],3]。
如何驗證?
通過id()
對比拷貝前后對象的內存地址,或直接用is
方法判斷拷貝前后對象內存地址是否相同。
驗證示例:
-
字符串淺拷貝
import copy a = "abc" b = copy.copy(a) print("a的內存地址:",id(a)) print("b的內存地址:",id(b))
輸出:
a的內存地址: 2140603563288 b的內存地址: 2140603563288
結論:未拷貝
-
數值淺拷貝
a = 12 b = copy.copy(a) print("a的內存地址:",id(a)) print("b的內存地址:",id(b))
輸出:
a的內存地址: 1896907216 b的內存地址: 1896907216
結論:未拷貝
-
列表淺拷貝
a = [1,2,3] b = copy.copy(a) print("a的內存地址:",id(a)) print("b的內存地址:",id(b))
輸出:
a的內存地址: 2140688273224 b的內存地址: 2140681134216
結論:拷貝成功,創建了新的內存地址
-
列表嵌套情況淺拷貝(特別注意)
a = [1,2,[3,4]] b = copy.copy(a) print("a:",a) print("a的內存地址:",id(a)) print("b:",b) print("b的內存地址:",id(b))
輸出:
a: [1, 2, [3, 4]] a的內存地址: 2140679959240 b: [1, 2, [3, 4]] b的內存地址: 2140679959112
外部地址改變,創建了新的內存地址
驗證內部嵌套列表是否拷貝:
In [43]: id(a[2]) Out[43]: 2140680066952 In [44]: id(b[2]) Out[44]: 2140680066952
結論:嵌套列表內存地址相同,未進行拷貝
再次驗證,嘗試修改a列表,是否對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列表的子列表會同時改變b列表的子列表。
結論:
符合淺拷貝的特性,淺拷貝只會對可變類型的第一層進行拷貝,而不會拷貝其可變類型的子對象,因此未拷貝部分指向的仍是同一個內存地址;
深拷貝
copy.deepcopy
deepcopy函數是深拷貝, 只要發現對象有可變類型就會對該對象到最后一個可變類型的每一層對象就行拷貝, 對每一層拷貝的對象都會開辟新的內存空間進行存儲。
深拷貝同樣無法拷貝不可變類型:字符串、數字、元組。
- 不可變類型的深拷貝(主要討論元組及其子元素)
# 不可變類型元組(需要特別注意)
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)
# 此處雖然外層元組是不可變類型,但內存地址依然改變了,原因是深拷貝會對所有可變子對象進行拷貝,因此內部列表會被拷貝,內存地址改變
In [65]: id(a)
Out[65]: 2140685431720
In [66]: id(b)
Out[66]: 2140688106408
# 其內部的子列表被拷貝,內存地址改變,由于元組是不可變類型,內部改變,其本身地址也會改變。
In [67]: id(a[2])
Out[67]: 2140681195272
In [68]: id(b[2])
Out[68]: 2140687283336
# 其內部的不可變對象無法拷貝,內存地址不變
In [69]: id(a[1])
Out[69]: 1896906896
In [70]: id(b[1])
Out[70]: 1896906896
? 結論:
? 不可變類型進行深拷貝如果子對象沒有可變類型則不會進行拷貝,而只 是拷貝了這個對象的引用,否則會對該對象到最后一個可變類型的每一層 對象就行拷貝, 對每一層拷貝的對象都會開辟新的內存空間進行存儲
-
可變類型的深拷貝(主要討論列表及子列表)
In [77]: a = [1,2,[1,2,3]] In [78]: b = copy.deepcopy(a) # 外部列表內存地址改變 In [79]: id(a) Out[79]: 2140687234824 In [80]: id(b) Out[80]: 2140681187208 # 子列表內存地址也改變 In [81]: id(a[2]) Out[81]: 2140681025608 In [82]: id(b[2]) Out[82]: 2140690147272 # 改變子列表元素不會再影響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]]
結論:
可變類型進行深拷貝會對該對象到最后一個可變類型的每一層對象就行拷貝, 對每一層拷貝的對象都會開辟新的內存空間進行存儲。
總結
- 淺拷貝使用copy.copy函數
- 深拷貝使用copy.deepcopy函數
- 不管是給對象進行深拷貝還是淺拷貝,只要拷貝成功就會開辟新的內存空間存儲拷貝的對象。
- 淺拷貝和深拷貝的區別是:
- 淺拷貝最多拷貝對象的一層,深拷貝可能拷貝對象的多層。