Python 可变对象的赋值

在使用 Python 列表时, 出现了修改其中一个列表, 其他列表联动改变这个情况, 在查阅文档后, 写在这里记录一下.

出现这个情况的原因是因为我使用了 a = b 这个赋值语句, 我下意识的认为 a, b 是两个不同的对象, 其实在 Python 一切都是对象, 所谓的变量, 只是指向一个对象的内存地址. 我们平时使用的赋值语句 = 其实只是让一个一个变量指向另一个变量指向的内存地址. 例如:

a = 1
b = a
print(a, b)
print(id(a))
print(id(b))
a = 2
print(a, b)

1 1
140712422177856
140712422177856
2 1

可以看到两个变量指向同一个内存地址, 这是 Python 解释器为了节省内存资源, 选择的一种方式. 可以看到, 当变量指向不可变类型时候, b 的值并不会随着 a 的变化而变化.

然而这种方式有一个弊端, 当我们面对可变类型时, 就会发生一些不可控的事情.

a = [1, 2, 3]
b = a
print(a, b)
print(id(a))
print(id(b))
a[0] = 4
print(a, b)

[1, 2, 3] [1, 2, 3]
2537876904712
2537876904712
[4, 2, 3] [4, 2, 3]

可以看到, 因为 a, b 两个变量指向的是同一个内存地址, 当我们修改 a 的数据时, b会一起改变. 常规情况下,这并不是我们想要的结果.

那么, 有没有办法把数据拷贝到一块新的内存地址中呢?

浅拷贝与深拷贝

这就需要用到 Python 内置库 copy. 我们需要用到这个库中的 copy, deepcopy 两个方法.

浅拷贝

我们使用 b = copy.copy(a) 进行浅拷贝, 执行这个语句后 a, b 是两个不同的对象, 指向不同的内存地址.

但是 a, b 中的子对象还是会指向同一地址, 例如下文中的 a 列表中的字典, b 列表中的字典就是子对象, 他们其实指向的是同一内存地址.

import copy


a = [1, 2, 3, {'a': 1, 'b': 2, 'c': 3}]
b = copy.copy(a)
print(a)
print(b)
print(id(b))
print(id(a))
a[0] = 2
print(a)
print(b)

[1, 2, 3, {'a': 1, 'b': 2, 'c': 3}]
[1, 2, 3, {'a': 1, 'b': 2, 'c': 3}]
2537876907720
2537876907400
[2, 2, 3, {'a': 1, 'b': 2, 'c': 3}]
[1, 2, 3, {'a': 1, 'b': 2, 'c': 3}]

可以看到, a, b 指向了不同的地址. 我们看下 a, b 中的子对象指向的内存地址

import copy


a = [1, 2, 3, {'a': 1, 'b': 2, 'c': 3}]
b = copy.copy(a)
print(id(a[3]))
print(id(b[3]))

2537876900904
2537876900904

可以看到, a, b 的子对象指向的内存. 因此, 当我们对这个字典进行操作时, 另一个数组对象中的也会随之变化, 原理同上.

那么, 有没有什么可以规避这一现象呢? 接下来我们走进深拷贝.

深拷贝

我们使用 b = copy.deepcopy(a) 进行深拷贝, 执行这个语句后 a, b 是两个不同的对象, 指向不同的内存地址. 且 a, b 的子对象也指向不同的内存地址, a, b 指向除了值相等, 完全没有任何关系的对象.

import copy


a = [1, 2, 3, {'a': 1, 'b': 2, 'c': 3, 'f': [1, 2, 3]}]
b = copy.deepcopy(a)
print(a)
print(b)
print(id(b))
print(id(a))
print(id(a[3]))
print(id(b[3]))
print(id(a[3]["f"]))
print(id(b[3]["f"]))
a[1] = 0
a[3]['d'] = 4
print(a)
print(b)

[1, 2, 3, {'a': 1, 'b': 2, 'c': 3, 'f': [1, 2, 3]}]
[1, 2, 3, {'a': 1, 'b': 2, 'c': 3, 'f': [1, 2, 3]}]
2537876918920
2537876918728
2537876851592
2537876851672
2537876916296
2537876919304
[1, 0, 3, {'a': 1, 'b': 2, 'c': 3, 'f': [1, 2, 3], 'd': 4}]
[1, 2, 3, {'a': 1, 'b': 2, 'c': 3, 'f': [1, 2, 3]}]

可以看到, 不论对象嵌套有多少个子对象, 进行深拷贝后得到的对象及其子对象, 都指向不同的地址. 同样的, 对其中任何数据的修改都不会影响到另一个对象.

可以看到, Python 为了适应不同的需求, 为我们提供了非常灵活的方法, 可以根据需求灵活使用.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

量慧实工作室LHStudio

LHStudio感谢您的支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值