不懂copy与deepcopy的区别?这一篇就够了

背景

在运用Python进行开发代码过程中,会遇到变量复制备份的场景,但并没有得到预期的结果,例如下面的例子:

lista = ['a', 'b', [1, 2, 3]]
listb = lista.copy()
lista[2].append(4)
 
print(lista)  # ['a', 'b', [1, 2, 3, 4]]
print(listb)  # ['a', 'b', [1, 2, 3, 4]]

代码本意是将lista复制给listb做个备份,再修改liasta,但是修改后发现listb也一并被修改了,没有达到备份的效果,这个是什么原因呢?

存储方式

首先了解一下Python的变量在内存中的存储方式。在基本数据类型中(包括set、list(tuple, str)、dict)都是采用引用的方式。

也就是说,每个变量都存储的是这个变量的地址,而不是值本身,就算更复杂的嵌套结构,也是存储是每个元素的地址而已,用一幅图来表示。

图片

如上图所示,用户看到的是 lista的4个元素值,但是内存中保存的却是4个元素地址。

当元素是列表时,第一层保存的是列表的地址,第二层保存的是列表元素的地址,第三层才是列表的值。当元素是字典的时候,与列表类似。

列表的增删改

在明白了变量存储方式后,继续看下内存下的增删改是怎么变化的。

列表修改已有值

新增一个内存块,再将引用的地址修改为新内存块的地址。

图片

列表新增一个值

新增一个内存块,新增一个地址引用。

在这里插入图片描述

列表整体重新赋值

删除变量地址和引用的值,新增地址和引用值的内存块。

在这里插入图片描述

copy与deepcopy的区别

基于以上的理解,再来看两种copy的区别就会更容易理解了,首先记住一个原则:
copy:不管多么复杂的数据结构,浅拷贝都只会copy一层。

deepcopy:将整个变量内存全部复制一遍,新变量与原变量没有任何关系。

举个例子来验证一下上面的结论:有如下的一段代码,最终的4个列表值是多少?

注意:引用deepcopy需要导入copy库。

import copy

a = [1, 2, 3, 4, ['a', 'b']]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)

a.append(5)
a[1] = 20
a[4].append('c')
del a[0]

print(a)
print(b)
print(c)
print(d)

列表b

表示b也引用的a的地址,两者引用的内存地址是一样的。因此b和a的关系是紧密相连的,一模一样。可以通过 id(a) 和id(b)比较,两者是一样的。

列表c

由于c是浅拷贝的a列表,因此只copy了第一层,也就是地址层。

所以,当a.append(5)时,新增了一个内存块,但是c只有前5个内存块,因此c没有变化。

继续a修改了a[1],然而这个值是属于第一层,已经copy给了c,因此c也没有变化。

继续a修改了子列表,这个时候a复制给c的只是列表的地址,且a中的子列表地址和c中的子列表地址是指向同一个地方的,因此修改了a中子列表,c中的子列表也会相应的改变。

最后删除a[0],与修改a[1]一致,与c无关。可以用图再说明一下。

在这里插入图片描述

列表d

由于d是深拷贝的a列表,因此d是将a的地址和值一并复制过来,与a没有半点关系,也就是说d和a是两个完全独立的内存块,没有任何交集。因此,后面a的任意修改都与d无关,用图表示如下。

在这里插入图片描述

因此,程序运行出来后的结果就是:

a:[20,3,4,[‘a’,‘b’,‘c’],5]
b:[20,3,4,[‘a’,‘b’,‘c’],5]
c:[1,2,3,4,[‘a’,‘b’,‘c’]]
d:[1,2,3,4,[‘a’,‘b’]]

总结

综上,我们在使用copy的时候,一定要记住:copy只是拷贝了第一层,而deepcopy才是拷贝的全部数据。

因此就不难发现,文章背景中的代码使用备份功能时,备份列表需要使用deepcopy,而不是简单的copy。


资源分享

下方这份完整的软件测试视频学习教程已经上传CSDN官方认证的二维码,朋友们如果需要可以自行免费领取 【保证100%免费】

在这里插入图片描述

在这里插入图片描述

  • 15
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中,`copy`和`deepcopy`都是用于复制对象的函数,但它们之间有一些区别。 `copy`是浅拷贝,它创建了一个新的对象,但是这个新对象仍然与原对象共享内部嵌套对象的引用。换句话说,新对象中的某些内部对象实际上是原对象中相同的对象。当你修改一个嵌套对象时,这个改变也会反映在其他拷贝中。 `deepcopy`是深拷贝,它创建了一个全新的对象,并且递归地复制原始对象及其所有嵌套对象。这意味着新对象与原对象以及其嵌套对象完全独立,对其中一个对象的任何改变都不会影响其他对象。 具体来说,`copy`只复制了原始对象的引用,而没有复制对象自身。而`deepcopy`创建了一个全新的对象,并且递归地复制所有嵌套对象。 下面是一个简单的示例来展示它们之间的区别: ```python import copy # 原始列表 original_list = [[1, 2, 3], [4, 5, 6]] # 浅拷贝 shallow_copy = copy.copy(original_list) # 深拷贝 deep_copy = copy.deepcopy(original_list) # 修改原始列表的第一个元素 original_list = 10 print(original_list) # 输出: [[10, 2, 3], [4, 5, 6]] print(shallow_copy) # 输出: [[10, 2, 3], [4, 5, 6]] print(deep_copy) # 输出: [[1, 2, 3], [4, 5, 6]] ``` 在上面的示例中,原始列表被修改后,浅拷贝和原始列表都发生了改变,而深拷贝保持了不变。这是因为浅拷贝只复制了嵌套列表的引用,而深拷贝创建了一个新的嵌套列表。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值