python 深拷貝和淺拷貝(shallow and deep copy)
阿新 • • 發佈:2018-12-22
1,深拷貝和淺拷貝只相對於容器型別的物件(compound obejects)來說,對於原子型別的物件(atomic objects)沒有這個概念。看下面的程式碼:
num = 123
num_c = num
print(id(num), id(num_c))
num_c = 456
print(id(num), id(num_c))
# 輸出為
# 4526750784 4526750784
# 4526750784 4563542224
string = 'abc' string_c = string print(id(string), id(string_c)) string_c = 'def' print(id(string), id(string_c)) # 輸出為 # 4528361176 4528361176 # 4528361176 4529762464
對於原子型別,賦值(assignment)只是建立了一個引用指向另一個物件,改變其中一個物件不會影響另一個物件。
2,對於容器物件,正常賦值(normal assignment)和切片賦值(slice assignment)效果不一樣。程式碼如下:
lis = ['a', 'b', 'c'] lis_c = lis print(id(lis), id(lis_c)) [print(x, id(x)) for x in lis] [print(x, id(x)) for x in lis_c] print('After') lis_c[1:3] = ['d', 'e'] print(id(lis), id(lis_c)) [print(x, id(x)) for x in lis] [print(x, id(x)) for x in lis_c] # 輸出為 4564651592 4564651592 a 4528834072 b 4528276568 c 4528219168 a 4528834072 b 4528276568 c 4528219168 After 4564651592 4564651592 a 4528834072 d 4528456400 e 4528388952 a 4528834072 d 4528456400 e 4528388952
lis = ['a', 'b', 'c'] lis_c = lis[:] print(id(lis), id(lis_c)) [print(x, id(x)) for x in lis] [print(x, id(x)) for x in lis_c] print('After') lis_c[1:3] = ['d', 'e'] print(id(lis), id(lis_c)) [print(x, id(x)) for x in lis] [print(x, id(x)) for x in lis_c] # 輸出為 4564225608 4564496008 a 4528834072 b 4528276568 c 4528219168 a 4528834072 b 4528276568 c 4528219168 After 4564225608 4564496008 a 4528834072 b 4528276568 c 4528219168 a 4528834072 d 4528456400 e 4528388952
lis = ['a', 'b', 'c']
lis_c = lis
print(id(lis), id(lis_c))
[print(x, id(x)) for x in lis]
[print(x, id(x)) for x in lis_c]
print('After')
lis_c = ['d', 'e', 'f']
print(id(lis), id(lis_c))
[print(x, id(x)) for x in lis]
[print(x, id(x)) for x in lis_c]
# 輸出為
4562685000 4562685000
a 4528834072
b 4528276568
c 4528219168
a 4528834072
b 4528276568
c 4528219168
After
4562685000 4564494984
a 4528834072
b 4528276568
c 4528219168
d 4528456400
e 4528388952
f 4527690168
可以看出,正常賦值lis_c和lis指向同樣的記憶體地址。對於正常賦值,改變lis_c的部分元素,lis的元素也會跟著改變;而對於切片賦值,二者指向不同的地址,所以不會相互影響。對正常賦值,如果改變整個lis_c中的元素,則lis_c的記憶體地址會發生改變,所以lis_c和lis不會相互影響。
3,字典的性質與列表相似,具有2中的性質,改變字典部分的key或全部的key,字典的記憶體地址會變;改變部分的value或全部的value,字典的記憶體地址不變。
dic = {'a': 1, 'b': 2, 'c': 3}
dic_c = dic
print(id(dic), id(dic_c))
[print('key:', x, id(x), 'value:', y, id(y)) for x, y in dic.items()]
[print('key:', x, id(x), 'value:', y, id(y)) for x, y in dic_c.items()]
print('change part of key')
dic_c = {'aa': 1, 'bb': 2, 'c': 3}
print(id(dic), id(dic_c))
[print('key:', x, id(x), 'value:', y, id(y)) for x, y in dic.items()]
[print('key:', x, id(x), 'value:', y, id(y)) for x, y in dic_c.items()]
# 輸出為
4564146576 4564146576
key: a 4528834072 value: 1 4526746880
key: b 4528276568 value: 2 4526746912
key: c 4528219168 value: 3 4526746944
key: a 4528834072 value: 1 4526746880
key: b 4528276568 value: 2 4526746912
key: c 4528219168 value: 3 4526746944
change part of key
4564146576 4564735176
key: a 4528834072 value: 1 4526746880
key: b 4528276568 value: 2 4526746912
key: c 4528219168 value: 3 4526746944
key: aa 4550712208 value: 1 4526746880
key: bb 4543935296 value: 2 4526746912
key: c 4528219168 value: 3 4526746944
dic = {'a': 1, 'b': 2, 'c': 3}
dic_c = dic
print(id(dic), id(dic_c))
[print('key:', x, id(x), 'value:', y, id(y)) for x, y in dic.items()]
[print('key:', x, id(x), 'value:', y, id(y)) for x, y in dic_c.items()]
print('change all values')
dic_c['a'] = 11
dic_c['b'] = 22
dic_c['c'] = 33
print(id(dic), id(dic_c))
[print('key:', x, id(x), 'value:', y, id(y)) for x, y in dic.items()]
[print('key:', x, id(x), 'value:', y, id(y)) for x, y in dic_c.items()]
# 輸出為
4564735320 4564735320
key: a 4528834072 value: 1 4526746880
key: b 4528276568 value: 2 4526746912
key: c 4528219168 value: 3 4526746944
key: a 4528834072 value: 1 4526746880
key: b 4528276568 value: 2 4526746912
key: c 4528219168 value: 3 4526746944
change all values
4564735320 4564735320
key: a 4528834072 value: 11 4526747200
key: b 4528276568 value: 22 4526747552
key: c 4528219168 value: 33 4526747904
key: a 4528834072 value: 11 4526747200
key: b 4528276568 value: 22 4526747552
key: c 4528219168 value: 33 4526747904
4,對於元祖來說,改變其中一個元素,就使整個元祖的記憶體地址發生改變。
tup = ('a', 'b', ['c', 'd'])
tup_c = tup
print(id(tup), id(tup_c))
[print(x, id(x)) for x in tup]
[print(x, id(x)) for x in tup_c]
print('After')
tup_c = ('a', 'b', ['c', 'e'])
print(id(tup), id(tup_c))
[print(x, id(x)) for x in tup]
[print(x, id(x)) for x in tup_c]
# 輸出為
4564433872 4564433872
a 4528834072
b 4528276568
['c', 'd'] 4564658312
a 4528834072
b 4528276568
['c', 'd'] 4564658312
After
4564433872 4564147008
a 4528834072
b 4528276568
['c', 'd'] 4564658312
a 4528834072
b 4528276568
['c', 'e'] 4564603272
5,通過下面三種方法可以建立淺拷貝: (1) taking a complete slice [:], (2) using a factory function, e.g., list(), dict(), etc., or (3) using the copy() function of the copy module。深拷貝要用到copy module中的copy.deepcopy()。
import copy
a = ['123', '456', ['a', 'b']]
b = copy.copy(a)
print(id(a), id(b))
b[2][0] = ['d']
print(a, id(a), b, id(b))
# 輸出為
4564727816 4564602952
['123', '456', [['d'], 'b']] 4564727816 ['123', '456', [['d'], 'b']] 4564602952
import copy
a = ['123', '456', ['a', 'b']]
b = copy.deepcopy(a)
print(id(a), id(b))
b[2][0] = ['d']
print(a, id(a), b, id(b))
# 輸出為
4564493896 4564658824
['123', '456', ['a', 'b']] 4564493896 ['123', '456', [['d'], 'b']] 4564658824
可以看出,無論淺拷貝還是深拷貝,list在賦值前後的記憶體地址不一樣,不同點是深拷貝會遞迴地為容器中的容器進行拷貝;淺拷貝只對最外層拷貝,內層的容器還是指向同一個地址,會相互影響。