1. 程式人生 > >python 深拷貝和淺拷貝(shallow and deep copy)

python 深拷貝和淺拷貝(shallow and deep copy)

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在賦值前後的記憶體地址不一樣,不同點是深拷貝會遞迴地為容器中的容器進行拷貝;淺拷貝只對最外層拷貝,內層的容器還是指向同一個地址,會相互影響。