Python的浅复制与深复制
- 对于不可变对象来说,因其值不可变,复制一个副本对它的作用不大。但对于可变对象来说,因其值可变,很多时候复制一个副本可以保存它未改变前的值,用来做对比或者做数据备份。
- 创建一个副本,这里涉及到浅复制和深复制。
浅复制与深复制的构造方法
- 首先都需要导入copy模块
- 浅复制:copy.copy(x)
- 深复制:copy.deepcopy(x)
其中浅复制还可以用切片实现:
- 例如:
a = [1,2,3] ; aa = a[:]
浅复制与深复制的区别
- 浅复制:复制内层容器时不会单独开辟空间,而是引用原来的地址。
- 深复制:复制内层不可变的容器时不会单独开辟空间,引用其原地址;若是内层可变的容器,则会单独开辟空间
容器:
- 可以在里面装下多个元素的,可以用in, not in关键字判断元素是否包含在容器中的。
- 常见的容器有:字符串、元组、列表、字典、集合。
http://www.pythontutor.com
是一个可以一边执行代码一边查看对象引用情况的网站。
我们构造一个含有内层容器的对象[0,1,[2,99],4]
,然后在线查看深、浅复制分别都是如何复制的。
1 | #构造对象,并进行浅复制和深复制 |
那么,改变原值时,浅复制和深复制的内容会如何改变?
1 | #构造含内层容器的对象 |
- 改变原值的内层容器里的值时,浅复制会随之改变,深复制则不会。
理解深浅复制的区别:
现给出一个列表
lsa = [12, 'abc', [8, 0]]
,对lsa
分别进行浅复制赋值给lsb
,进行深复制赋值给lsc
,然后改lsa
中的[8, 0]
为[9, 0]
,请问lsb
和lsc
中的值改变了吗?为什么?1
2
3
4
5
6
7
8
9
10
11list_a = [1, "hello", [2, 3]]
print(id(list_a[2]))
list_b = copy.copy(list_a)
print(id(list_b[2]))
list_c = copy.deepcopy(list_a)
print(id(list_c[2]))
list_a[2] = [4,5]
print(id(list_a[2]))
print(id(list_b[2]))
print(id(list_c[2]))在执行上述操作后,
lsb
中的子列表会随之改变,而lsc
中的子列表则保持不变。具体分析如下:lsb = copy.copy(lsa)
创建了列表的浅副本。浅复制会复制顶层对象,但嵌套的子对象(如列表[8, 0]
)仍指向原对象。因此,lsb
和lsa
的顶层元素是独立的,但它们共享嵌套的子列表。lsc = copy.deepcopy(lsa)
创建了列表的深副本。深复制会递归复制所有嵌套对象,生成完全独立的对象树。因此,lsc
中的子列表是lsa
中子列表的独立副本。
所以执行当执行
lsa[2] = [9, 0]
时:lsb
的变化:lsb
的顶层结构与lsa
分离,但共享子列表。修改lsa[2]
只是替换了lsa
的第三个元素的引用,而lsb[2]
仍指向原嵌套列表[8, 0]
。因此,lsb
中的值不会改变。lsc
的变化: 深复制生成的lsc
完全独立于lsa
。修改lsa[2]
不会影响lsc
的任何元素。因此,lsc
中的值保持不变。
结论:
lsb
中的值:[8, 0]
(未改变)lsc
中的值:[8, 0]
(未改变)- 原因:浅复制共享嵌套对象,而深复制完全独立。修改
lsa
的子列表引用不会传播到复制对象。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 ErgouTree's Blog!
评论
FPS: 60