Python 可变对象的赋值

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 为了适应不同的需求, 为我们提供了非常灵活的方法, 可以根据需求灵活使用.

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » Python 可变对象的赋值