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 相关文章推荐
高性能web服务器框架Tornado简单实现restful接口及开发实例
Jul 16 Python
用Python写一段用户登录的程序代码
Apr 22 Python
详谈python在windows中的文件路径问题
Apr 28 Python
Python3.6实现连接mysql或mariadb的方法分析
May 18 Python
Python把csv数据写入list和字典类型的变量脚本方法
Jun 15 Python
Python爬虫常用小技巧之设置代理IP
Sep 13 Python
django开发post接口简单案例,获取参数值的方法
Dec 11 Python
Django实现基于类的分页功能
Oct 31 Python
Python捕获异常堆栈信息的几种方法(小结)
May 18 Python
python dict乱码如何解决
Jun 07 Python
Numpy中np.max的用法及np.maximum区别
Nov 27 Python
matlab xlabel位置的设置方式
May 21 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堆栈与列队的学习
2013/06/21 PHP
yii操作cookie实例简介
2014/07/09 PHP
php自定义apk安装包实例
2014/10/20 PHP
关于php开启错误提示的总结
2019/09/24 PHP
Swoole源码中如何查询Websocket的连接问题详解
2020/08/30 PHP
js之WEB开发调试利器:Firebug 下载
2007/01/13 Javascript
JavaScript window.setTimeout() 的详细用法
2009/11/04 Javascript
setTimeout和setInterval的深入理解
2013/11/08 Javascript
基于jQuery实现放大镜特效
2020/10/19 Javascript
BootStrap按钮标签及基本样式
2016/11/23 Javascript
小程序开发实战:实现九宫格界面的导航的代码实现
2017/01/19 Javascript
angularjs实现首页轮播图效果
2017/04/14 Javascript
详解如何使用 vue-cli 开发多页应用
2017/12/16 Javascript
Vue中 v-if 和v-else-if页面加载出现闪现的问题及解决方法
2018/10/12 Javascript
js中位运算的运用实例分析
2018/12/11 Javascript
使用Python读写文本文件及编写简单的文本编辑器
2016/03/11 Python
对Python中列表和数组的赋值,浅拷贝和深拷贝的实例讲解
2018/06/28 Python
对numpy.append()里的axis的用法详解
2018/06/28 Python
转换科学计数法的数值字符串为decimal类型的方法
2018/07/16 Python
Python对切片命名的实现方法
2018/10/16 Python
对python创建及引用动态变量名的示例讲解
2018/11/10 Python
Python实现Mysql数据统计及numpy统计函数
2019/07/15 Python
pycharm如何实现跨目录调用文件
2020/02/28 Python
Pytorch数据拼接与拆分操作实现图解
2020/04/30 Python
宝塔面板出现“open_basedir restriction in effect. ”的解决方法
2021/03/14 PHP
CSS3按钮鼠标悬浮实现光圈效果源码
2016/09/11 HTML / CSS
美国的Eastbay旗下的运动款子品牌:Final-Score
2018/01/01 全球购物
教师年终个人自我评价
2013/10/04 职场文书
师范应届生求职信
2013/11/15 职场文书
测控技术与仪器个人求职信范文
2013/12/30 职场文书
道德模范先进事迹
2014/02/14 职场文书
见习期自我鉴定范文
2014/03/19 职场文书
2014年党员学习“三严三实”思想汇报
2014/09/15 职场文书
公务员党的群众路线教育实践活动学习心得体会
2014/10/30 职场文书
小学信息技术教学反思
2016/02/16 职场文书
使用numpy nonzero 找出非0元素
2021/05/14 Python