[ python ] 深淺拷貝

首先要了解什麼是拷貝、淺拷貝、深拷貝?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值沒有變。大功告成!

相關文章
相關標籤/搜索