[開篇四:Python零散知識拾荒之深淺拷貝]2018-11-06

Python當(dāng)中對于拷貝,分為兩種類型。一種是數(shù)字和字符串,另一種就是列表、元組、字典等其他類型了。

一、數(shù)字和字符串的拷貝

1、賦值

舉個栗子:

a1 = 123123
a2 = 123123
# a2 = a1  # 賦值
print(id(a1))  # 通過id()函數(shù)來打印變量在內(nèi)存當(dāng)中的地址
print(id(a2))

輸出結(jié)果是:

1959780298352
1959780298352 

??在以上代碼塊當(dāng)中,a2與a1所賦的值是一樣的,都是數(shù)字123123。因?yàn)閜ython有一個重用機(jī)制,對于同一個數(shù)字,python并不會開辟一塊新的內(nèi)存空間,而是維護(hù)同一塊內(nèi)存地址,只是將該數(shù)字對應(yīng)的內(nèi)存地址的引用賦值給變量a1和a2。所以根據(jù)輸出結(jié)果,a1和a2其實(shí)對應(yīng)的是同一塊內(nèi)存地址,只是兩個不同的引用罷了。同樣的,對于a2 = a1,其實(shí)效果等同于“a1 = 123123; a2 = 123123”,它也就是將a1指向123123的引用賦值給a2。字符串跟數(shù)字的原理雷同,如果把123123改成“abcabc”也是一樣的。*

結(jié)論:對于通過用 = 號賦值,數(shù)字和字符串 在內(nèi)存當(dāng)中用的都是同一塊地址。

2、淺拷貝

import copy  # 使用淺拷貝需要導(dǎo)入copy模塊
 
a1 = 123123
a3 = copy.copy(a1)  # 使用copy模塊里的copy()函數(shù)就是淺拷貝了
print(id(a1))
print(id(a3))

輸出結(jié)果是:

35233168
35233168

??通過使用copy模塊里的copy()函數(shù)來進(jìn)行淺拷貝,把a(bǔ)1拷貝一份賦值給a3,查看輸出結(jié)果發(fā)現(xiàn),a1和a3的內(nèi)存地址還是一樣。

結(jié)論:對于淺拷貝,數(shù)字和字符串在內(nèi)存當(dāng)中用的也是同一塊地址。

3、深拷貝

舉個栗子:

import copy

a1 = 123123
a4 = copy.deepcopy(a1)  # 深拷貝是用copy模塊里的deepcopy()函數(shù)
print(id(a1))
print(id(a4))

輸出結(jié)果為:

31432080
31432080

查看結(jié)果發(fā)現(xiàn),對于深拷貝,數(shù)字和字符串在內(nèi)存當(dāng)中用的也是同一塊地址。

所以綜上所述,對于數(shù)字和字符串的賦值、淺拷貝、深拷貝在內(nèi)存當(dāng)中用的都是同一塊地址。原理如下圖:

image

二、字典、列表、元組等其他類型的拷貝

1、賦值

舉個栗子:

n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]}
n2 = n1  # 賦值
print(id(n1))
print(id(n2))

輸出結(jié)果:

2235551677536
2235551677536

??我們的栗子當(dāng)中用了一個字典n1,字典里面嵌套了一個列表,當(dāng)我們把n1賦值給n2時,內(nèi)存地址并沒有發(fā)生變化,因?yàn)槠鋵?shí)它也是只是把n1的引用拿過來賦值給n2而已。(我們用了一個字典來舉例,其他類型也是一樣的)

原理如下圖:

image.png

結(jié)論:對于賦值,字典、列表、元組等其他類型用的內(nèi)存地址不會變化。

2、淺拷貝

舉個栗子:

import copy

n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]}
n3 = copy.copy(n1)  # 淺拷貝
print("第一層字典的內(nèi)存地址:")
print(id(n1))
print(id(n3))
print("第二層嵌套的列表的內(nèi)存地址:")
print(id(n1["k3"]))
print(id(n3["k3"]))

輸出結(jié)果:

第一層字典的內(nèi)存地址:
6516024
6516096
第二層嵌套的列表的內(nèi)存地址:
36995720
36995720

??通過以上結(jié)果可以看出,進(jìn)行淺拷貝時,我們的字典第一層n1和n3指向的內(nèi)存地址已經(jīng)改變了,但是對于第二層里的列表并沒有拷貝,它的內(nèi)存地址還是一樣的。原理如下圖:

image

結(jié)論:所以對于淺拷貝,字典、列表、元組等類型,它們只拷貝第一層地址

3、深拷貝

舉個栗子:

import copy

n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]}
n4 = copy.deepcopy(n1)  # 深拷貝
print("第一層字典的內(nèi)存地址:")
print(id(n1))
print(id(n4))
print("第二層嵌套的列表的內(nèi)存地址:")
print(id(n1["k3"]))
print(id(n4["k3"]))

輸出結(jié)果:

第一層字典的內(nèi)存地址:
31157560
35463600
第二層嵌套的列表的內(nèi)存地址:
35947144
35947336

??通過以上結(jié)果發(fā)現(xiàn),進(jìn)行深拷貝時,字典里面的第一層和里面嵌套的地址都已經(jīng)變了。對于深拷貝,它會拷貝多層,將第二層的列表也拷貝一份,如果還有第三層嵌套,那么第三層的也會拷貝,但是對于里面的最小元素,比如數(shù)字和字符串,這里就是“wu”,123,“alex”,678之類的,按照python的機(jī)制,它們會共同指向同一個位置,它的內(nèi)存地址是不會變的。原理如下圖:

image

結(jié)論:對于深拷貝,字典、列表、元組等類型,它里面嵌套多少層,就會拷貝多少層出來,但是最底層的數(shù)字和字符串地址不變。

舉個實(shí)際應(yīng)用場景的栗子。

我們在維護(hù)服務(wù)器信息的時候,經(jīng)常會要更新服務(wù)器信息,這時我們重新一個一個添加是比較麻煩的,我們可以把原數(shù)據(jù)類型拷貝一份,在它的基礎(chǔ)上做修改。

栗子一、使用淺拷貝

import copy


dic = {
    "cpu": [80, ],
    "mem": [80, ],
    "disk": [80, ]
}
# 定義了一個字典,存儲服務(wù)器信息。
print('before', dic)
new_dic = copy.copy(dic)
new_dic['cpu'][0] = 50  # 更新cpu為50
print(dic)
print(new_dic)

輸出結(jié)果為:

before {'cpu': [80], 'mem': [80], 'disk': [80]}
{'cpu': [50], 'mem': [80], 'disk': [80]}
{'cpu': [50], 'mem': [80], 'disk': [80]}

這時我們會發(fā)現(xiàn),使用淺拷貝時,我們修改新的字典的值之后,原來的字典里面的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)

輸出結(jié)果:

before {'cpu': [80], 'mem': [80], 'disk': [80]}
{'cpu': [80], 'mem': [80], 'disk': [80]}
{'cpu': [50], 'mem': [80], 'disk': [80]}

??使用深拷貝的時候,發(fā)現(xiàn)只有新的字典的cpu值被修改了,原來的字典里面的cpu值沒有變。大功告成!

總結(jié)

  • 賦值(=):數(shù)據(jù)完全共享(賦值是在內(nèi)存中指向同一個對象,如果是可變(mutable)類型,比如列表,修改其中一個,另一個必定改變;如果是不可變類型(immutable),比如字符串,修改了其中一個,另一個并不會變)
  • 淺拷貝:數(shù)據(jù)半共享(復(fù)制其數(shù)據(jù)獨(dú)立內(nèi)存存放,但是只拷貝成功第一層)
  • 深拷貝:數(shù)據(jù)完全不共享(復(fù)制其數(shù)據(jù)完完全全放獨(dú)立的一個內(nèi)存,完全拷貝,數(shù)據(jù)不共享);深拷貝就是完完全全復(fù)制了一份,且數(shù)據(jù)不會互相影響,因?yàn)閮?nèi)存不共享。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 可變(mutable)和不可變(immutable)參數(shù) 不可變對象在進(jìn)行重新賦值的時候,實(shí)際上是將原始值丟棄,將...
    樓程智閱讀 1,326評論 1 1
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 30,192評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,621評論 1 32
  • php -m windows 下查看php已開啟的拓展 GMP是The GNU MP Bignum Libra...
    jianghu000閱讀 2,401評論 0 0
  • 俗話說救人一命,勝造七級浮屠 一、 月圓之夜,大部分的人都已早早下班,和自己的親朋好友聚在一起,聊天喝酒賞圓月。然...
    2B繪畫鉛筆閱讀 352評論 1 0

友情鏈接更多精彩內(nèi)容