Python當中對於拷貝,分為兩種類型。一種是數字和字符串,另一種就是列表、元組、字典等其他類型了。
一、數字和字符串的拷貝
1、賦值
舉個栗子:
a1 = 123123 a2 = 123123 # a2 = a1 # 賦值 print(id(a1)) # 通過id()函數來打印變量在內存當中的地址 print(id(a2))
輸出結果是:
1959780298352 1959780298352
在以上代碼塊當中,a2與a1所賦的值是一樣的,都是數字123123。因為python有一個重用機制,對於同一個數字,python並不會開辟一塊新的內存空間,而是維護同一塊內存地址,只是將該數字對應的內存地址的引用賦值給變量a1和a2。所以根據輸出結果,a1和a2其實對應的是同一塊內存地址,只是兩個不同的引用罷了。同樣的,對於a2 = a1,其實效果等同於“a1 = 123123; a2 = 123123”,它也就是將a1指向123123的引用賦值給a2。字符串跟數字的原理雷同,如果把123123改成“abcabc”也是一樣的。
結論:對於通過用 = 號賦值,數字和字符串在內存當中用的都是同一塊地址。
2、淺拷貝
import copy # 使用淺拷貝需要導入copy模塊 a1 = 123123 a3 = copy.copy(a1) # 使用copy模塊里的copy()函數就是淺拷貝了 print(id(a1)) print(id(a3))
輸出結果是:
35233168 35233168
通過使用copy模塊里的copy()函數來進行淺拷貝,把a1拷貝一份賦值給a3,查看輸出結果發現,a1和a3的內存地址還是一樣。
結論:對於淺拷貝,數字和字符串在內存當中用的也是同一塊地址。
3、深拷貝
舉個栗子:
import copy a1 = 123123 a4 = copy.deepcopy(a1) # 深拷貝是用copy模塊里的deepcopy()函數 print(id(a1)) print(id(a4))
輸出結果為:
31432080 31432080
查看結果發現,對於深拷貝,數字和字符串在內存當中用的也是同一塊地址。
所以綜上所述,對於數字和字符串的賦值、淺拷貝、深拷貝在內存當中用的都是同一塊地址。原理如下圖:
二、字典、列表、元組等其他類型的拷貝
1、賦值
舉個栗子:
n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]} n2 = n1 # 賦值 print(id(n1)) print(id(n2))
輸出結果:
2235551677536 2235551677536
我們的栗子當中用了一個字典n1,字典里面嵌套了一個列表,當我們把n1賦值給n2時,內存地址並沒有發生變化,因為其實它也是只是把n1的引用拿過來賦值給n2而已。(我們用了一個字典來舉例,其他類型也是一樣的)
原理如下圖:
結論:對於賦值,字典、列表、元組等其他類型用的內存地址不會變化。
2、淺拷貝
舉個栗子:
import copy n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]} n3 = copy.copy(n1) # 淺拷貝 print("第一層字典的內存地址:") print(id(n1)) print(id(n3)) print("第二層嵌套的列表的內存地址:") print(id(n1["k3"])) print(id(n3["k3"]))
輸出結果:
第一層字典的內存地址: 6516024 6516096 第二層嵌套的列表的內存地址: 36995720 36995720
通過以上結果可以看出,進行淺拷貝時,我們的字典第一層n1和n3指向的內存地址已經改變了,但是對於第二層里的列表並沒有拷貝,它的內存地址還是一樣的。原理如下圖:
結論:所以對於淺拷貝,字典、列表、元組等類型,它們只拷貝第一層地址。
3、深拷貝
舉個栗子:
import copy n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]} n4 = copy.deepcopy(n1) # 深拷貝 print("第一層字典的內存地址:") print(id(n1)) print(id(n4)) print("第二層嵌套的列表的內存地址:") print(id(n1["k3"])) print(id(n4["k3"]))
輸出結果:
第一層字典的內存地址: 31157560 35463600 第二層嵌套的列表的內存地址: 35947144 35947336
通過以上結果發現,進行深拷貝時,字典里面的第一層和里面嵌套的地址都已經變了。對於深拷貝,它會拷貝多層,將第二層的列表也拷貝一份,如果還有第三層嵌套,那么第三層的也會拷貝,但是對於里面的最小元素,比如數字和字符串,這里就是“wu”,123,“alex”,678之類的,按照python的機制,它們會共同指向同一個位置,它的內存地址是不會變的。原理如下圖:
結論:對於深拷貝,字典、列表、元組等類型,它里面嵌套多少層,就會拷貝多少層出來,但是最底層的數字和字符串地址不變。
舉個實際應用場景的栗子。
我們在維護服務器信息的時候,經常會要更新服務器信息,這時我們重新一個一個添加是比較麻煩的,我們可以把原數據類型拷貝一份,在它的基礎上做修改。
栗子一、使用淺拷貝
import copy dic = { "cpu": [80, ], "mem": [80, ], "disk": [80, ] } # 定義了一個字典,存儲服務器信息。 print('before', dic) new_dic = copy.copy(dic) new_dic['cpu'][0] = 50 # 更新cpu為50 print(dic) print(new_dic)
輸出結果為:
before {'cpu': [80], 'mem': [80], 'disk': [80]} {'cpu': [50], 'mem': [80], 'disk': [80]} {'cpu': [50], 'mem': [80], 'disk': [80]}
這時我們會發現,使用淺拷貝時,我們修改新的字典的值之后,原來的字典里面的cpu值也被修改了,這並不是我們希望看到的。
栗子二、使用深拷貝
import copy dic = { "cpu": [80, ], "mem": [80, ], "disk": [80, ] } print('before', dic) new_dic = copy.deepcopy(dic) new_dic['cpu'][0] = 50 print(dic) print(new_dic)
輸出結果:
before {'cpu': [80], 'mem': [80], 'disk': [80]} {'cpu': [80], 'mem': [80], 'disk': [80]} {'cpu': [50], 'mem': [80], 'disk': [80]}
使用深拷貝的時候,發現只有新的字典的cpu值被修改了,原來的字典里面的cpu值沒有變。大功告成!
注:本文是根據老男孩課程內容整理而成的,圖片也是出自武sir的,因為原文我找不到了,所以下面貼一下武sir的博客地址。本文僅供個人筆記使用,如果有侵犯,請聯系我,我立即撤銷。
武sir博客地址:http://www.cnblogs.com/wupeiqi/