首先要了解什麼是拷貝、淺拷貝、深拷貝?python
拷貝:服務器
從原始數據複製一份出來,當複製成功後,這兩份數據都是相互獨立的,即修改任意一份數據都不會影響另外一份數據。app
淺拷貝:函數
python中,淺拷貝就是隻是拷貝最外層的類型,簡單來說就是拷貝了引用,並無拷貝內容. copy.copy()spa
深拷貝:code
對於一個對象全部層次的拷貝(遞歸拷貝)copy.deepcopy()對象
要知道深淺拷貝的區別,首先要知道python中什麼是 可變數據類型 和 不可變數據類型blog
不可變數據類型的定義:遞歸
python中的不可變數據類型,不容許變量的值發生變化,若是改變了變量的值,至關因而新建了一個對象,而對於相同的值的對象,在內存中則只有一個對象,內部會有一個引用計數來記錄有多少個變量引用這個對象.內存
python中 不可變數據類型:
可變數據類型的定義:
可變數據類型,容許變量的值發生變化,即若是對變量進行append、+=等這種操做後,只是改變了變量的值,而不會新建一個對象,變量引用的對象的地址也不會變化,不過對於相同的值的不一樣對象,在內存中則會存在不一樣的對象,即每一個對象都有本身的地址, 至關於內存中對於同值的對象保存了多份,這裏不存在引用計數,是實實在在的對象。
python中 可變數據類型:
經過python中數據類型的分類,咱們談論如下幾種的拷貝:
不可變數據類型:
可變數據類型:
1. 賦值
咱們已知python中不可變數據類型:整型、浮點數、字符串、布爾值、元組
In [1]: a1 = 123 # 整型 In [2]: a2 = a1 In [3]: id(a1), id(a2) Out[3]: (1562980880, 1562980880) In [4]: b1 = 1.123 # 浮點數 In [5]: b2 = b1 In [6]: id(b1), id(b2) Out[6]: (1503028953024, 1503028953024) In [7]: c1 = 'hello' # 字符串 In [8]: c2 = c1 In [9]: id(c1), id(c2) Out[9]: (1503040484272, 1503040484272) In [10]: d1 = True # 布爾值 In [11]: d2 = d1 In [12]: id(d1), id(d2) Out[12]: (1562722720, 1562722720) In [13]: e1 = (1, 2, 3, 'hkey') # 元組 In [14]: e2 = e1 In [15]: id(e1), id(e2) Out[15]: (1503040349032, 1503040349032)
經過以上的例子,a一、a2 賦值的值是同樣的。由於python有一個重用機制,對於 不可變數據類型 來講,python並不會開闢一塊新的內存空間,而是維護同一塊內存地址,只是將 不可變數據類型 對應的地址引用賦值給變量a一、a2。因此根據輸出結果,a1和a2其實對應的是同一塊內存地址,只是兩個不一樣的引用。
結論:對於 不可變數據類型 經過'='賦值,不可變數據類型在內存當中用的都是同一塊地址。
2. 淺拷貝
In [1]: import copy In [2]: a1 = 3.14 In [3]: a2 = copy.copy(a1) In [4]: id(a1), id(a2) Out[4]: (1690132070168, 1690132070168)
經過使用copy模塊裏的copy()函數來進行淺拷貝,把a1拷貝一份賦值給a2,查看輸出結果發現,a1和a2的內存地址仍是同樣。
結論:對於 不可變數據類型 經過淺拷貝,不可變數據類型在內存當中用的都是同一塊地址。
3. 深拷貝
In [1]: import copy In [2]: a1 = 'hello' In [3]: a2 = copy.deepcopy(a1) In [4]: id(a1), id(a2) Out[4]: (1645307287064, 1645307287064)
經過使用copy模塊裏的deepcopy()函數來進行深拷貝,把a1拷貝一份賦值給a2,查看輸出結果發現,a1和a2的內存地址仍是同樣。
結論:對於 不可變數據類型 經過深拷貝,不可變數據類型在內存當中用的都是同一塊地址。
這裏能夠對 不可變數據類型 下一個定義了:
對於python中 不可變數據類型 的賦值、淺拷貝、深拷貝在內存中都是指向同一內存地址。以下圖:
1. 賦值
In [1]: n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]} In [2]: n2 = n1 In [3]: id(n1), id(n2) Out[3]: (1888346752712, 1888346752712)
在上面的例子中,咱們使用了列表嵌套列表的方式,賦值後內存空間地址是一致的。其中原理以下圖:
結論:對於 可變數據類型 進行賦值內存地址是不會變化的。
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"])) 執行結果: 第一層字典的內存地址: 2260623665800 2260625794440 第二層嵌套的列表的內存地址: 2260626131144 2260626131144
經過以上結果能夠看出,進行淺拷貝時,咱們的字典第一層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"])) 執行結果: 第一層字典的內存地址: 2018569398920 2018574527176 第二層嵌套的列表的內存地址: 2018576058568 2018576056840
經過以上結果發現,進行深拷貝時,字典裏面的第一層和裏面嵌套的地址都已經變了。對於深拷貝,它會拷貝多層,將第二層的列表也拷貝一份,
若是還有第三層嵌套,那麼第三層的也會拷貝,可是對於裏面的最小元素,好比數字和字符串,這裏就是「wu」,123,「alex」,678之類的,
按照python的機制,它們會共同指向同一個位置,它的內存地址是不會變的。原理以下圖:
結論:對於 python 可變數據類型,它裏面嵌套多少層,就會拷貝多少層出來,可是最底層的數字和字符串不變。
python 深淺拷貝的實例:
咱們在維護服務器信息的時候,常常會要更新服務器信息,這時咱們從新一個一個添加是比較麻煩的,咱們能夠把原數據類型拷貝一份,在它的基礎上作修改。
實例1:使用淺拷貝
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值也被修改了,這並非咱們但願看到的。
實例2:使用深拷貝
import copy # 定義了一個字典,存儲服務器信息 dic = { 'cpu':[80, ], 'mem':[80, ], 'disk':[80, ] } print('before', dic) new_dic = copy.deepcopy(dic) new_dic['cpu'][0] = 50 # 更新 cpu 爲 50 print(dic) print(new_dic) 執行結果: before {'cpu': [80], 'disk': [80], 'mem': [80]} {'cpu': [80], 'disk': [80], 'mem': [80]} {'cpu': [50], 'disk': [80], 'mem': [80]}
使用深拷貝的時候,發現只有新的字典的cpu值被修改了,原來的字典裏面的cpu值沒有變。大功告成!