python 中的深淺復制
前言
想起來寫這篇博客是因為這段時間學習 js 的時候涉及到了變量的深淺復制問題,然后想先把 python 中的深淺復制理解的更深入一些,再寫 js 中的深淺復制,因為 python 對我來說已經很熟悉了。
在 python 中,標識一個對象的唯一身份有三個狀態:對象的 id(內存地址),對象類型,對象值。
賦值
- 賦值是將一個對象的地址賦值給一個變量,使得變量指向該內存地址;
- 修改不可變對象時(str、tuple、int)需要開辟新的內存空間;
- 修改可變對象時(list、dict、set)不需要開辟新的內存空間。
賦值是將 id 重新賦值給了一個新的變量,引用計數加1。
淺拷貝
淺拷貝只是拷貝父對象,而對於父對象中的子對象並不會進行拷貝。
a = {1: [1, 2, 3]}
b = a.copy()
a['name'] = 'musibii'
a # {1: [1, 2, 3], 'name': 'musibii'}
b # {1: [1, 2, 3]}
a[1].append(4)
a # {1: [1, 2, 3, 4], 'name': 'musibii'}
b # {1: [1, 2, 3, 4]}
也就是說淺拷貝將a的值復制到一個新的內存空間,並將內存地址賦值給 b,所以對 a 對象添加新的屬性,b 並不會改變;但是因為淺拷貝只是拷貝了一層,對於子對象的內存空間是原對象的內存引用,所以修改 a 相應的 b 中也會改變。
淺拷貝只拷貝父對象,不會拷貝對象內部的子對象。
深拷貝
深拷貝完全賦值被復制對象的元素,不是復制內存地址,是開辟新的內存空間將被復制對象的值放在了新的內存空降中,並將新的內存地址指向了新的變量,這樣的話,修改原對象不會對新的對象產生影響。
深拷貝是在另一塊地址中創建一個新的變量,同時容器內的元素的地址也是新開辟的,僅僅是值相同而已,是完全的副本。
在Python 中深拷貝需要引入 copy 模塊:
import copy
c = copy.deepcopy(a)
c # {1: [1, 2, 3], 'name': 'musibii'}
a[1].append(4)
a # {1: [1, 2, 3, 4], 'name': 'musibii'}
c # {1: [1, 2, 3], 'name': 'musibii'}
解析
- b = a:賦值引用,a 和 b 都指向同一個對象
- b = a.copy():淺拷貝,a 和 b 是一個獨立的對象,但他們的子對象還是指向同一個對象(引用)。
- b = copy.deepcopy(a):深拷貝,a 和 b 完全拷貝了父對象及其子對象,兩者完全獨立。
更多實例
import copy
a = [1, 2, 3, 4, ['a', 'b']] #原始對象
b = a #賦值,傳對象的引用
c = copy.copy(a) #對象拷貝,淺拷貝
d = copy.deepcopy(a) #對象拷貝,深拷貝
a.append(5) #修改對象a
a[4].append('c') #修改對象a中的['a', 'b']數組對象
print( 'a = ', a )
print( 'b = ', b )
print( 'c = ', c )
print( 'd = ', d )
輸出
('a = ', [1, 2, 3, 4, ['a', 'b', 'c'], 5])
('b = ', [1, 2, 3, 4, ['a', 'b', 'c'], 5])
('c = ', [1, 2, 3, 4, ['a', 'b', 'c']])
('d = ', [1, 2, 3, 4, ['a', 'b']])