Python 可变对象的赋值

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

我们提供的服务有:网站建设、网站设计、微信公众号开发、网站优化、网站认证、梧州ssl等。为上千企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的梧州网站制作公司

出现这个情况的原因是因为我使用了 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


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]


[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}]


[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]))



可以看到, 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]}]






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


网站名称:Python 可变对象的赋值
网站路径:http://scyanting.com/article/dsoghpj.html