python-深浅拷贝
深浅拷贝是两种不同的创建新对象的方式,区别在于在复制对象时是否复制其子对象。浅拷贝只复制的对象本身,而不复制其子对象(直接拿过来用,所以子对象的内存地址相同)。而深拷贝会递归复制对象及其子对象(子对象也会有新的内存地址)。
在浅拷贝中,如果原始对象嵌套中包含可变对象(列表、字典),那么拷贝之后它和原对象中的嵌套对象拥有相同的内存地址,也就是共享数据,修改掉一个那么另一个也会随即修改。在深拷贝中深拷贝创建一个对象,并递归复制原始对象所有的子对象,这意味着原对象中如果包含了可变类型,新对象将拥有其副本,它们之间并不会共享数据,所以之间也不受任何影响。
简而言之,就是如果原始对象包含可变对象,则浅拷贝会共享这些可变对象,而深拷贝会创建这些可变对象的副本。
1.浅拷贝:
1.1 针对不可变类型的数据类型(字符串、整形、浮点型、布尔值),浅拷贝的数据对象和原数据对象是相同的内存地址:
import copy
a = 'max'
b = copy.copy(a)
# print(a,b) max max
# print(id(a),id(b)) # 2056738009712 2056738009712
由以上的代码得出结论:针对不可变类型的浅拷贝,只是换了一个名字,对象在内存中的地址是不变的。
1.2 针对可变类型的浅拷贝(不存在嵌套类型的可变数据类型):
列表:
import copy
list1 = [1,2,3,4]
list2 = copy.copy(list1)
# print(list2) # [1, 2, 3, 4]
# print(id(list1),id(list2)) # 2090444888384 2090444885184
# print(id(list1[2]),id(list2[2])) # 2009830261040 2009830261040
根据以上例子可以看出:
列表本身的浅拷贝得到对象的地址和原对象的地址是不同的,因为列表是可变数据类型。两个列表中的第一个元素相同,因为整形是不可变类型,所以第一个元素的内存地址相同。

字典:
dict1 = {'name': 'max', 'age': 18}
dict2 = copy.copy(dict1)
# print(dict1,dict2) # {'name': 'max', 'age': 18} {'name': 'max', 'age': 18}
# print(id(dict1),id(dict2)) # 1845058977728 1845058994432
# print(id(dict1['name']),id(dict2['name'])) # 1991927624304 1991927624304
字典也存在相同的情况:
由于字典是可变数据类型,浅拷贝之后的内存地址和原内存地址不同。但由于字典的键和值都是不可变数据类型,所以相同的键对应的值内存地址相同。
1.3 针对可变类型嵌套可变类型:
list1 = [1, 2, [1, 2, 3]]
list2 = copy.copy(list1)
# print(list1,list2) # [1, 2, [1, 2, 3]] [1, 2, [1, 2, 3]]
# print(id(list1), id(list2)) # 2727419481280 2727419481536
# print(id(list1[1]),id(list2[1])) # 1909107327248 1909107327248
# print(id(list1[2]),id(list2[2])) # 2249792198976 2249792198976
# print(id(list1[2][1]),id(list2[2][1])) # 1879449534736 1879449534736
在可变的数据类型中嵌套可变数据类型,浅拷贝只复制最外层的数据,也就是外层列表的内存地址会发生改变,而列表中嵌套的整形和列表(可变类型和不可变类型)内存地址都不会发生改变。
如果原始对象嵌套中包含可变对象(列表、字典),那么拷贝之后它和原对象中的嵌套对象拥有相同的内存地址,也就是共享数据,修改掉一个那么另一个也会随即修改:
import copy
l1 = [1,2,3,[1,2,3]]
l2 = copy.copy(l1)
l2[3][0] = 2
print(l1,l2)
# [1, 2, 3, [2, 2, 3]] [1, 2, 3, [2, 2, 3]]
2.深拷贝
2.1 不可变类型得到的深拷贝:
a = 'max'
b = copy.copy(a)
c = copy.deepcopy(a)
print(id(a),id(b),id(c))
# 1430455874160 1430455874160 1430455874160
针对字符串,深拷贝或者浅拷贝都不会改变其内存地址。并且深浅拷贝结果一致
2.2 可变类型的深拷贝(无嵌套情况)
不存在嵌套情况:
针队列表:
list1 = [1, 2, 3]
list2 = copy.copy(list1)
list3 = copy.deepcopy(list1)
print(id(list1), id(list2), id(list3))
# 2251232876864 2251232873664 2251232873920
当可变类型中数据都为不可变类型时,深浅拷贝都会改变原来数据的内存地址,并且深浅拷贝的结果不一致
针队字典结论相同:
dict1 = {'name':'max','age':18}
dict2 = copy.copy(dict1)
dict3 = copy.deepcopy(dict1)
print(id(dict1),id(dict2),id(dict3))
# 1893476581312 1893476598016 1893476598144
dict1 = {'name': 'max', 'age': 18}
dict2 = copy.copy(dict1)
dict3 = copy.deepcopy(dict1)
print(id(dict1['age']), id(dict2['age']), id(dict3['age']))
# 3049950741264 3049950741264 3049950741264
得出结论:
1.深拷贝对最外层拷贝数据时会开辟新的内存地址来存放数据
2.深拷贝对内层不可变数据类型直接复制数据类型和地址,内存地址不会改变,和可变类型内层的浅拷贝效果相同。
2.3 可变类型对的深拷贝(嵌套情况):
l1 = [1,2,[1,2,3]]
l2 = copy.copy(l1)
l3 = copy.deepcopy(l1)
print(id(l1),id(l2),id(l3))
# 2690575273152 2690575273408 2690575276736
print(id(l1[1]),id(l2[1]),id(l3[1]))
# 1609890464016 1609890464016 1609890464016
嵌套情况最外层可变类型深浅拷贝内存地址都会发生变化,内层不可变数据类型深浅拷贝内存地址都不会发生改变
l1 = [1,2,[1,2,3]]
l2 = copy.copy(l1)
l3 = copy.deepcopy(l1)
print(id(l1[2]),id(l2[2]),id(l3[2]))
# 1620485577024 1620485577024 1620485574912
嵌套情况内层为可变类型时,浅拷贝会连同数据和内存地址一起拷贝,所以浅拷贝后内层可变类型内存地址不会发生改变。而深拷贝会改变内层可变类型的内存地址。
原对象中如果包含了可变类型,新对象将拥有其副本,它们之间并不会共享数据,所以之间也不受任何影响:
import copy
l1 = [1,2,3,[1,2,3]]
l2 = copy.deepcopy(l1)
l2[3][0] = 2
print(l1,l2)
# [1, 2, 3, [1, 2, 3]] [1, 2, 3, [2, 2, 3]]

浙公网安备 33010602011771号