天天看點

python中的指派與深淺拷貝一、數字和字元串的拷貝二、字典、清單、元組等其他類型的拷貝

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
           

  檢視結果發現,對于深拷貝,數字和字元串在記憶體當中用的也是同一塊位址。

是以綜上所述,對于數字和字元串的指派、淺拷貝、深拷貝在記憶體當中用的都是同一塊位址。原理如下圖:

python中的指派與深淺拷貝一、數字和字元串的拷貝二、字典、清單、元組等其他類型的拷貝

二、字典、清單、元組等其他類型的拷貝

1、指派

舉個栗子:

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

輸出結果:

2235551677536
2235551677536
           

  我們的栗子當中用了一個字典n1,字典裡面嵌套了一個清單,當我們把n1指派給n2時,記憶體位址并沒有發生變化,因為其實它也是隻是把n1的引用拿過來指派給n2而已。(我們用了一個字典來舉例,其他類型也是一樣的)

原理如下圖:

python中的指派與深淺拷貝一、數字和字元串的拷貝二、字典、清單、元組等其他類型的拷貝

結論:對于指派,字典、清單、元組等其他類型用的記憶體位址不會變化。

 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指向的記憶體位址已經改變了,但是對于第二層裡的清單并沒有拷貝,它的記憶體位址還是一樣的。原理如下圖:

python中的指派與深淺拷貝一、數字和字元串的拷貝二、字典、清單、元組等其他類型的拷貝

 結論:是以對于淺拷貝,字典、清單、元組等類型,它們隻拷貝第一層位址。

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的機制,它們會共同指向同一個位置,它的記憶體位址是不會變的。原理如下圖:

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/