python :赋值、深拷贝、浅拷贝的区别

昨天在Leetcode上遇见一道有关回溯算法的题目遇见了这个问题:

在这里插入图片描述
问题:

  1. 在3处将得到的cur使用append操作加1处的res中。
  2. 然后对cur使用pop操作。
  3. 结果储存在res的cur也被修改了。
  4. 而如果使用图中的切片操作则可以避免这个问题。

这就涉及到了深拷贝,浅拷贝。

1.赋值

在讲深拷贝之前,先看一下python的赋值操作, 也就是等于:

a = [1, 2, 3, 4]
b = a

print("a的地址:", id(a))
print("b的地址:", id(b))

打印结果:

a的地址: 1949097742728
b的地址: 1949097742728

可以看见,进行赋值操作的时候,变量a和变量b都指向了同一个地址,也就是说,只是并没有创建一个新的 list 容器,a和b本质上是一个容器,它们装着同样的物质。这样,如果将a中的物质给换掉,那么b中的物质肯定做了相同的变化,因为它们本质上就是叫法不同的同一个容器而已。

# 改变a装的东西
a[0] = "我变了"

print("我是a:", a)
print("我是b:", b)

所以它们做相同的变化:

我是a: ['我变了', 2, 3, 4]
我是b: ['我变了', 2, 3, 4]

2.浅拷贝

在开头提到的列表切片操作就是浅拷贝,所以什么是浅拷贝?
直观的将,如果b是a浅拷贝而来,那么a和b就不再是同一个容器了,而是两个不同的容器。

上代码:

a = [1, 2, 3, 4]
b = a[:]             #浅拷贝

# 改变a装的东西
a[0] = "我变了"

print("我是a:", a)
print("我是b:", b)

结果:

我是a: ['我变了', 2, 3, 4]
我是b: [1, 2, 3, 4]

这个时候,改变a的元素0, a的元素0发生了改变,但是b的元素0没有发生改变。那么问题来了,a容器和b容器装的东西是不是一样的?

我们看:

a = [1, 2, 3, 4]
b = a[:]             #浅拷贝

print("我是a的元素地址:", [id(i) for i in a])
print("我是b的元素地址:", [id(i) for i in b])
我是a的元素地址: [140725704499600, 140725704499632, 140725704499664, 140725704499696]
我是b的元素地址: [140725704499600, 140725704499632, 140725704499664, 140725704499696]

容器装的物质完全一样,那为什么改变a的元素0,b的元素不会改变呢?
答案就是:因为它们是两个不同的容器(对象)!
一开始,它们装的元素是一样的,也就是它们指向了相同的东西。但是,你换了a的元素,那a容器的元素0就指向了其他的东西,而b容器和a容器压根是两个容器,因此b的元素指向的地址不会发生改变。

可以看见,容器a和容器b的元素0的指向是不一样的, 而其他元素的指向是一样的。

我是a的元素地址: [2195726848528  , 140726275449264, 140726275449296, 140726275449328]
我是b的元素地址: [140726275449232, 140726275449264, 140726275449296, 140726275449328]

但是浅拷贝有一个问题,让a中发生发生某种改变的时候,b也发生了某种改变:
这种情况就是容器里面包含容器(可变类型):

a = [1, 2, 3, [-1, -2, -3]]    #元素3换成另外一个list(可变类型)
b = a[:]                      #浅拷贝

print("我是a的元素地址:", [id(i) for i in a])
print("我是b的元素地址:", [id(i) for i in b])


a[3][0] = '我是容器a里面容器的元素, 我变了'

print("我是a:", a)
print("我是b:", b)

print("我是a的元素地址:", [id(i) for i in a])
print("我是b的元素地址:", [id(i) for i in b])

打印结果:
首先是元素地址:

我是a的元素地址: [140726275449232, 140726275449264, 140726275449296, 2156915020168]
我是b的元素地址: [140726275449232, 140726275449264, 140726275449296, 2156915020168]

然后看a变了之后,b变了没有:

我是a: [1, 2, 3, ['我是容器a里面容器的元素, 我变了', -2, -3]]
我是b: [1, 2, 3, ['我是容器a里面容器的元素, 我变了', -2, -3]]

b 也跟着发生变化,解释就是a中元素3是一个容器, b中元素3也是一个容器,这两个容器就是一个相同的容器。还记得上面的赋值操作吗?同一个容器,对元素进行替换,容器里面的元素肯定发生一样的变化!这种现象对应的就是赋值。

所以,为了避免这种现象,就有了深拷贝。

3.深拷贝

先上代码:

import copy

a = [1, 2, 3, [-1, -2, -3]]
b = copy.deepcopy(a)                      #深拷贝

这时候,容器a和容器b和浅拷贝一样,不是同一个容器:

print(id(a))
print(id(b))
2417826338760
2417826338504

再看看里面装的东西:

print("我是a的元素地址:", [id(i) for i in a])
print("我是b的元素地址:", [id(i) for i in b])

我是a的元素地址: [140725724815760, 140725724815792, 140725724815824, 2417826118280]
我是b的元素地址: [140725724815760, 140725724815792, 140725724815824, 2417826107208]

注意到,元素中的不可变类型和浅拷贝一样,指向同样的地址,但是可变元素,则换成了不同的容器(元素3的地址不一样了),这不就是对容器里面的可变元素(容器)进行了浅拷贝吗?因此,现在改变a容器的元素3的一个元素,则b不再进行相应变化:

a[3][0] = '我是容器a里面容器的元素, 我变了'

print("我是a:", a)
print("我是b:", b)
我是a: [1, 2, 3, ['我是容器a里面容器的元素, 我变了', -2, -3]]
我是b: [1, 2, 3, [-1, -2, -3]]

总结:赋值、浅拷贝、深拷贝一层套一层,有种盗梦空间的感觉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值