玩命加载中 . . .

深拷贝与浅拷贝


Python标签

res = []
def func(obj):
    res.append(obj)

a = [1, 2, 3]
print(id(a))        # 2897774181960
func(a)
print(id(res[0]))   # 2409845052424
print("res: ", res)

a.append(4)
print("res: ", res)

res:  [[1, 2, 3]]
res:  [[1, 2, 3, 4]]

从输出可以看到,ares[0]id一样,说明指向了同一块内存空间,所以a改变了之后res[0]也跟着改变了,你以为拷贝了,其实并没有

原因:

Python没有变量,我们平时所说的变量其实只是标签,是引用
执行:values=[0,1,2]的时候,python做的事情是首先创建一个列表对象[0,1,2],然后给它贴上名为values的标签。如果随后执行values=[3,4,5]的话,python做的事情是创建另一个列表对象[3,4,5],然后把刚才那张名为values的标签从前面的[0,1,2]对象上撕下来,重新贴到[3,4,5]这个对象上。

所以,这里的ares[0]就相当于两张标签贴到了同一个对象[1,2,3]

浅拷贝

解决办法是用拷贝,这里先说浅拷贝shallow copy

注意:[:]生成对象的拷贝或者是复制序列,不再是引用和共享变量,但此法只能顶层复制

res = []
def func(obj):
    res.append(obj[:])  # 这里不一样

a = [1, 2, 3]
print(id(a))        # 1558831191048
func(a)
print(id(res[0]))   # 1558832493512
print("res: ", res)

a.append(4)
print("a: ", a)
print("res: ", res)

res:  [[1, 2, 3]]
a:  [1, 2, 3, 4]
res:  [[1, 2, 3]]

可以看到ares[0]id不一样了,a改变了之后,res[0]没有改变,对于这种场景,浅拷贝就适用了,但如果包含子对象,浅拷贝就有问题了

a = [0, [1, 2], 3]
b = a[:]

a[0] = 8
a[1][1] = 9

print(a)  # [8, [1, 9], 3]
print(b)  # [0, [1, 9], 3]

可以看到,b[1][1]也跟着改变了,这就是浅拷贝只做顶层拷贝的效果

原因从图里就可以看出来了,a[1]b[1]还是指向了同一个对象

深拷贝

解决办法使用深拷贝

import copy
a = [0, [1, 2], 3]
b = copy.deepcopy(a)

print(id(a[1]))  # 2071720571784
print(id(b[1]))  # 2071721298184

a[0] = 8
a[1][1] = 9

print(a)  # [8, [1, 9], 3]
print(b)  # [0, [1, 2], 3]

深拷贝之后就指向两个完全不同的对象了

对象也是一样

import copy
class Person:
    def __init__(self):
        self.num = [1, 2]

p1 = Person()
print(p1.num)   # [1, 2]
p2 = p1         # 指向同一个内存
print(p2.num)   # [1, 2]
p3 = copy.deepcopy(p1)  # 深拷贝
print(p3.num)   # [1, 2]

p1.num.append(3)
print(p1.num)   # [1, 2, 3]
print(p2.num)   # [1, 2, 3] 也改变了
print(p3.num)   # [1, 2] 没有改变

文章作者: kunpeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kunpeng !
  目录