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在赋值前后的内存地址不一样,不同点是深拷贝会递归地为容器中的容器进行拷贝;浅拷贝只对最外层拷贝,内层的容器还是指向同一个地址,会相互影响。

猜你喜欢

转载自blog.csdn.net/linkequa/article/details/85069137