深浅拷贝

一、示例

import copy

a = [11, 22]
b = a
print(id(a))  # 17812744
print(id(b))  # 17812744
c = copy.deepcopy(a)
print(id(a))  # 17812744
print(id(c))  # 17810952
print(a)  # [11, 22]
print(c)  # [11, 22]
a.append(33)
print(a)  # [11, 22, 33]
print(c)  # [11, 22]

可以看到a、b内存地址是一样,可如下图表示;而c是完完全全拷贝了一份a指向的内容,这就是深拷贝。

二、深浅拷贝

浅拷贝:copy.copy()

import copy

a = [11, 22]
b = [33, 44]
c = [a, b]
d = copy.copy(c)
print(id(c))  # 17811464
print(id(d))  # 17405960
a.append(55)
print(c)  # [[11, 22, 55], [33, 44]]
print(d)  # [[11, 22, 55], [33, 44]]

把c里的东西取出来,然后d指向它,大致如下图所示。

深拷贝:copy.deepcopy()

import copy

a = [11, 22]
b = [33, 44]
c = [a, b]
d = copy.deepcopy(c)
print(id(c))  # 17942536
print(id(d))  # 17943112
a.append(55)
print(c)  # [[11, 22, 55], [33, 44]]
print(d)  # [[11, 22], [33, 44]]

深拷贝:我已经完完全全拷贝过来了,你改你的东西,不关我事。

三、其他

import copy

a = [11, 22]
b = [33, 44]
c = [a, b]
d = copy.copy(c)
e = copy.deepcopy(c)
print(id(c))  # 17680392
print(id(d))  # 17275144
print(id(e))  # 17681736
c.append([55, 66])
print(c)  # [[11, 22], [33, 44], [55, 66]]
print(d)  # [[11, 22], [33, 44]]
print(e)  # [[11, 22], [33, 44]]

深拷贝、浅拷贝:往c里添加数据,不关我事。

四、坑

如果copy.copy()拷贝的是元祖,那么不会进行浅拷贝。因为元祖是不可变类型,意味着数据一定不能修改,因此用copy.copy()的时候会自动判断,如果拷贝的是元祖,仅仅是指向这个元祖。

import copy

a = [11, 22]
b = copy.copy(a)
print(id(a))  # 17878344
print(id(b))  # 17471496
a = (11, 22)
b = copy.copy(a)
print(id(a))  # 17454856
print(id(b))  # 17454856
c = a
print(id(c))  # 17454856

对于上面这种情况,copy.deepcopy()同理。但是,请看下面代码:

import copy

a = [11, 22]
b = [33, 44]
c = (a, b)
d = copy.copy(c)
print(id(c))  # 12080904
print(id(d))  # 12080904
e = copy.deepcopy(c)
print(id(c))  # 12080904
print(id(e))  # 12095880
print(c)  # ([11, 22], [33, 44])
print(e)  # ([11, 22], [33, 44])
a.append(55)
print(c)  # ([11, 22, 55], [33, 44])
print(e)  # ([11, 22], [33, 44])

所以, 如果用copy.cpoy()、copy.deepcopy()对一个全部都是不可变类型的数据进行拷贝,那么它们的结果相同,都是指向;如果拷贝的是一个拥有不可变类型的数据,即使元祖是最顶层,那么copy.cpoy()依然是指向,而copy.deepcopy()这时则是深拷贝了。

五、切片也是浅拷贝

a = [11, 22]
b = [33, 44]
c = [a, b]
d = c[:]
print(id(c))  # 17750024
print(id(d))  # 17751752
print(id(c[0]))  # 17751688
print(id(d[0]))  # 17751688
print(a)  # [11, 22]
a.append(55)
print(c)  # [[11, 22, 55], [33, 44]]
print(d)  # [[11, 22, 55], [33, 44]]

d=c[:]d=copy.copy(c) 一样,属于浅拷贝。 

六、字典的copy()方法

d = {"name": "pd", "age": 18}
c = d.copy()
print(id(d))  # 6662152
print(id(c))  # 6662280
# 验证
d = {"name": "pd", "age": 18, "xx": [11, 22]}
c = d.copy()
d["xx"].append(33)
print(d)  # {'age': 18, 'xx': [11, 22, 33], 'name': 'pd'}
print(c)  # {'age': 18, 'xx': [11, 22, 33], 'name': 'pd'}
# 这说明了,使用字典copy()方法的时候,字典里指向的value是共享的。

七、应用

import copy

a = [11, 22]

def func(a):
    a.append(33)

func(a)
print(a)  # [11, 22, 33]

func(copy.deepcopy(a))
print(a)  # [11, 22, 33]
"""
打印发现没有再次添加33,说明了copy.deepcopy(a)把a复制了一份,
将复制的新列表传入函数内,对其进行操作,而没有影响到函数外的a
"""
func(a)
print(a)  # [11, 22, 33, 33]

你想拷贝列表、字典、元祖、对象等等,想拷贝什么就拷贝什么,关键在于,你想干什么?比如你从网上好不容易爬取到了某些数据,忘了保存,就进行测试了。如果你使用了深拷贝的话,这样在测试时即使污染了数据,也不会对源数据产生影响。

原文地址:https://www.cnblogs.com/believepd/p/10410019.html