Python-copy()与deepcopy()区别详解


Posted in Python onJuly 12, 2019

最近在实习,boss给布置了一个python的小任务,学习过程中发现copy()和deepcopy()这对好基友实在是有点过分,搞的博主就有点傻傻分不清啊,但是呢本着一探到底的精神,还是要查资料搞清楚这对好基友的区别。

其实呢,copy()与deepcopy()之间的区分必须要涉及到python对于数据的存储方式。

首先直接上结论:

—?我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。

—?而浅复制并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。这就和我们寻常意义上的复制有所不同了。

对于简单的 object,用 shallow copy 和 deep copy 没区别

复杂的 object, 如 list 中套着 list 的情况,shallow copy 中的 子list,并未从原 object 真的「独立」出来。也就是说,如果你改变原 object 的子 list 中的一个元素,你的 copy 就会跟着一起变。这跟我们直觉上对「复制」的理解不同。

看不懂文字没关系我们来看代码:

>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin 里边有三个元素:1, 2,[3, 4]
>>> cop1 = copy.copy(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False 
#cop1 和 cop2 看上去相同,但已不再是同一个object
>>> origin[2][0] = "hey!" 
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
#把origin内的子list [3, 4] 改掉了一个元素,观察 cop1 和 cop2

可以看到 cop1,也就是 shallow copy 跟着 origin 改变了。而 cop2 ,也就是 deep copy 并没有变。

似乎 deep copy 更加符合我们对「复制」的直觉定义: 一旦复制出来了,就应该是独立的了。如果我们想要的是一个字面意义的「copy」,那就直接用 deep_copy 即可。

那么为什么会有 shallow copy 这样的「假」 copy 存在呢? 这就是有意思的地方了。

python的数据存储方式

Python 存储变量的方法跟其他 OOP 语言不同。它与其说是把值赋给变量,不如说是给变量建立了一个到具体值的 reference。

当在 Python 中 a = something 应该理解为给 something 贴上了一个标签 a。当再赋值给 a 的时候,就好象把 a 这个标签从原来的 something 上拿下来,贴到其他对象上,建立新的 reference。 这就解释了一些 Python 中可能遇到的诡异情况:

>> a = [1, 2, 3]
>>> b = a
>>> a = [4, 5, 6] //赋新的值给 a
>>> a
[4, 5, 6]
>>> b
[1, 2, 3]
# a 的值改变后,b 并没有随着 a 变

>>> a = [1, 2, 3]
>>> b = a
>>> a[0], a[1], a[2] = 4, 5, 6 //改变原来 list 中的元素
>>> a
[4, 5, 6]
>>> b
[4, 5, 6]
# a 的值改变后,b 随着 a 变了

上面两段代码中,a 的值都发生了变化。区别在于,第一段代码中是直接赋给了 a 新的值(从 [1, 2, 3] 变为 [4, 5, 6]);而第二段则是把 list 中每个元素分别改变。

而对 b 的影响则是不同的,一个没有让 b 的值发生改变,另一个变了。怎么用上边的道理来解释这个诡异的不同呢?

首次把 [1, 2, 3] 看成一个物品。a = [1, 2, 3] 就相当于给这个物品上贴上 a 这个标签。而 b = a 就是给这个物品又贴上了一个 b 的标签。 

第一种情况:

a = [4, 5, 6] 就相当于把 a 标签从 [1 ,2, 3] 上撕下来,贴到了 [4, 5, 6] 上。

在这个过程中,[1, 2, 3] 这个物品并没有消失。 b 自始至终都好好的贴在 [1, 2, 3] 上,既然这个 reference 也没有改变过。 b 的值自然不变。

第二种情况:

a[0], a[1], a[2] = 4, 5, 6 则是直接改变了 [1, 2, 3] 这个物品本身。把它内部的每一部分都重新改装了一下。内部改装完毕后,[1, 2, 3] 本身变成了 [4, 5, 6]。

而在此过程当中,a 和 b 都没有动,他们还贴在那个物品上。因此自然 a b 的值都变成了 [4, 5, 6]。

搞明白这个之后就要问了,对于一个复杂对象的浅copy,在copy的时候到底发生了什么?
再看一段代码:

>>> import copy
>>> origin = [1, 2, [3, 4]]
#origin 里边有三个元素:1, 2,[3, 4]
>>> cop1 = copy.copy(origin)
>>> cop2 = copy.deepcopy(origin)
>>> cop1 == cop2
True
>>> cop1 is cop2
False 
#cop1 和 cop2 看上去相同,但已不再是同一个object
>>> origin[2][0] = "hey!" 
>>> origin
[1, 2, ['hey!', 4]]
>>> cop1
[1, 2, ['hey!', 4]]
>>> cop2
[1, 2, [3, 4]]
#把origin内的子list [3, 4] 改掉了一个元素,观察 cop1 和 cop2

学过docker的人应该对镜像这个概念不陌生,我们可以把镜像的概念套用在copy上面。 

copy对于一个复杂对象的子对象并不会完全复制,什么是复杂对象的子对象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是复杂对象的子对象。对于子对象,python会把它当作一个公共镜像存储起来,所有对他的复制都被当成一个引用,所以说当其中一个引用将镜像改变了之后另一个引用使用镜像的时候镜像已经被改变了。

所以说看这里的origin[2],也就是 [3, 4] 这个 list。根据 shallow copy 的定义,在 cop1[2] 指向的是同一个 list [3, 4]。那么,如果这里我们改变了这个 list,就会导致 origin 和 cop1 同时改变。这就是为什么上边 origin[2][0] = “hey!” 之后,cop1 也随之变成了 [1, 2, [‘hey!', 4]]。

deepcopy的时候会将复杂对象的每一层复制一个单独的个体出来。

这时候的 origin[2] 和 cop2[2] 虽然值都等于 [3, 4],但已经不是同一个 list了。即我们寻常意义上的复制。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python 用户登录验证的小例子
Mar 06 Python
Python中的左斜杠、右斜杠(正斜杠和反斜杠)
Aug 30 Python
python读取文本中数据并转化为DataFrame的实例
Apr 10 Python
redis之django-redis的简单缓存使用
Jun 07 Python
python中itertools模块zip_longest函数详解
Jun 12 Python
利用pandas将numpy数组导出生成excel的实例
Jun 14 Python
python绘制热力图heatmap
Mar 23 Python
Python一句代码实现找出所有水仙花数的方法
Nov 13 Python
Ubuntu中配置TensorFlow使用环境的方法
Apr 21 Python
解决Keyerror ''acc'' KeyError: ''val_acc''问题
Jun 18 Python
Python3.7安装PyQt5 运行配置Pycharm的详细教程
Oct 15 Python
Python实现粒子群算法的示例
Feb 14 Python
新手入门Python编程的8个实用建议
Jul 12 #Python
python读取并写入mat文件的方法
Jul 12 #Python
numpy数组广播的机制
Jul 12 #Python
Python的numpy库下的几个小函数的用法(小结)
Jul 12 #Python
python读取.mat文件的数据及实例代码
Jul 12 #Python
如何用Python破解wifi密码过程详解
Jul 12 #Python
python pip源配置,pip配置文件存放位置的方法
Jul 12 #Python
You might like
PHP memcache扩展的三种安装方法
2009/04/26 PHP
php 数组的指针操作实现代码
2011/02/08 PHP
php上传excel表格并获取数据
2017/04/27 PHP
windows 2008r2+php5.6.28环境搭建详细过程
2019/06/18 PHP
为Extjs加加速(javascript加速)
2010/08/19 Javascript
JavaScript高级程序设计 阅读笔记(十三) js定义类或对象
2012/08/14 Javascript
javascript中不等于的代码是什么怎么写
2013/12/29 Javascript
JS实现新浪博客左侧的Blog管理菜单效果代码
2015/10/22 Javascript
js右下角弹出提示框示例代码
2016/01/12 Javascript
JavaScript+CSS实现的可折叠二级菜单实例
2016/02/29 Javascript
Bootstrap中的Panel和Table全面解析
2016/06/13 Javascript
javascript实现随机生成DIV背景色
2016/06/20 Javascript
javascript实现的左右无缝滚动效果
2016/09/19 Javascript
JS根据生日月份和日期计算星座的简单实现方法
2016/11/24 Javascript
基于javascript实现最简单选项卡切换
2017/02/01 Javascript
基于require.js的使用(实例讲解)
2017/09/07 Javascript
vue获取dom元素注意事项
2017/12/28 Javascript
解决element UI 自定义传参的问题
2018/08/22 Javascript
electron中使用bootstrap的示例代码
2018/11/06 Javascript
你可能不知道的CORS跨域资源共享
2019/03/13 Javascript
原生javascript的ajax请求及后台PHP响应操作示例
2020/02/24 Javascript
Element-ui树形控件el-tree自定义增删改和局部刷新及懒加载操作
2020/08/31 Javascript
Python转码问题的解决方法
2008/10/07 Python
Python中用Ctrl+C终止多线程程序的问题解决
2013/03/30 Python
Python实现的redis分布式锁功能示例
2018/05/29 Python
python飞机大战pygame游戏背景设计详解
2019/12/17 Python
python通过安装itchat包实现微信自动回复收到的春节祝福
2020/01/19 Python
利用CSS3的定位页面元素
2009/08/29 HTML / CSS
前端canvas水印快速制作(附完整代码)
2019/09/19 HTML / CSS
HTML5如何为形状图上颜色怎么绘制具有颜色和透明度的矩形
2014/06/23 HTML / CSS
基于zepto的插件之移动端无缝向上滚动并上下触摸滑动实例代码
2016/12/20 HTML / CSS
小学生国庆节演讲稿
2014/09/05 职场文书
离职证明格式样本
2015/06/12 职场文书
奠基仪式致辞
2015/07/30 职场文书
公文写作:教你写“建议书”
2019/05/07 职场文书
Vue组件更新数据v-model不生效的解决
2022/04/02 Vue.js