簡析 Python 深拷貝和淺拷貝

簡介

Python 中賦值語句不復制對象,而是在目標和對象之間創建綁定 (Bindings) 關係。

示例:

In [1]: a = [1, 2, 3]

In [2]: b = a

In [3]: a
Out[3]: [1, 2, 3]

In [4]: b
Out[4]: [1, 2, 3]

In [5]: id(a)
Out[5]: 87660264

In [6]: id(b)
Out[6]: 87660264

變量 ab id 相同,也就說明他們指向同一地址,b 重複的引用了 a 指向的這個對象。

瞭解一下

Python 對象分爲可變對象和不可變對象。可變對象是指,對象的內容是可變的。而不可變的對象則相反,表示其內容不可變。其區分可變對象與不可變對象其實就是通過對象是否可哈希來區分的。不可變對象是可哈希類型,可變對象是不可哈希類型。

In [1]: hash(1)
Out[1]: 1

In [2]: hash(1.5)
Out[2]: 1073741825

In [3]: hash(True)
Out[3]: 1

In [4]: hash("123")
Out[4]: 2090756218

In [5]: hash((1,2,3))
Out[5]: -2022708474

In [6]: hash([1,2,3])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-35e31e935e9e> in <module>
----> 1 hash([1,2,3])

TypeError: unhashable type: 'list'

In [7]: hash({1,2,3})
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-d5ba4eb1a90a> in <module>
----> 1 hash({1,2,3})

TypeError: unhashable type: 'set'

In [8]: hash({"A": 1})
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-3d5562cfa16c> in <module>
----> 1 hash({"A": 1})

TypeError: unhashable type: 'dict'

內置類型不可變對象包括:

  • int

  • float

  • bool

  • str

  • tuple

  • frozenset

內置類型可變對象包括:

  • list

  • set

  • dict

淺拷貝

當我們想通過賦值來獲得一個新的對象,Python 給我們提供了一個方法 copy() ,通過此方法賦值的方式稱爲淺拷貝或淺層拷貝。

示例:

In [1]: a = [1, 2, 3]

In [2]: b = a.copy()

In [3]: a
Out[3]: [1, 2, 3]

In [4]: b
Out[4]: [1, 2, 3]

In [5]: id(a)
Out[5]: 82380744

In [6]: id(b)
Out[6]: 85989288

In [7]: a.append(4)

In [8]: a
Out[8]: [1, 2, 3, 4]

In [9]: b
Out[9]: [1, 2, 3]

這樣我們會獲得一個與 a 內容一致新變量,其在內存中分別指向兩個地址。

深拷貝

先看個例子:

In [1]: a = [1, 2, [3, 4]]

In [2]: b = a.copy()

In [3]: a[2].append(5)

In [4]: a.append(6)

In [5]: a
Out[5]: [1, 2, [3, 4, 5], 6]

In [6]: b
Out[6]: [1, 2, [3, 4, 5]]
    
In [7]: id(a[2])
Out[7]: 80479944

In [8]: id(b[2])
Out[8]: 80479944

這並沒有真正的新變量,b 只拷貝的最外層的內容,而內層的內容是直接引用的。另外,像這種列表中引用另一個列表的的形式,被稱爲複合對象。更準確的說,包含其他對象的對象就是複合對象。

如果想將內層的內容也作爲新變量的一部分,需要用到標準庫 copy 中的 deepcopy() 方法,通過此方法賦值的方式稱爲深拷貝或深層拷貝。

示例:

In [1]: import copy

In [2]: a = [1, 2, [3, 4]]

In [3]: b = copy.deepcopy(a)

In [4]: a[2].append(5)

In [5]: a.append(6)

In [6]: a
Out[6]: [1, 2, [3, 4, 5], 6]

In [7]: b
Out[7]: [1, 2, [3, 4]]
    
In [8]: id(a[2])
Out[8]: 75039880

In [9]: id(b[2])
Out[9]: 78604328    

淺拷貝和深拷貝的區別

淺層拷貝和深層拷貝之間的區別僅與複合對象相關:

  • 一個淺層拷貝會構造一個新的複合對象,然後(在可能的範圍內)將原對象中找到的 引用插入其中。
  • 一個深層拷貝會構造一個新的複合對象,然後遞歸地將原始對象中所找到的對象的 副本插入。

可變對象與不可變對象的淺拷貝和深拷貝區別:

拷貝類型 可變對象 不可變對象
淺拷貝 只拷貝外層元素,不影響內層元素 拷貝爲新對象
深拷貝 拷貝所有元素,包括內層元素和外層元素 拷貝爲新對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章