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常用模块介绍
Nov 21 Python
Python实现从订阅源下载图片的方法
Mar 11 Python
Python利用Beautiful Soup模块搜索内容详解
Mar 29 Python
简单学习Python多进程Multiprocessing
Aug 29 Python
python3+PyQt5+Qt Designer实现扩展对话框
Apr 20 Python
查看django版本的方法分享
May 14 Python
PyQt5+requests实现车票查询工具
Jan 21 Python
python爬取Ajax动态加载网页过程解析
Sep 05 Python
python中安装django模块的方法
Mar 12 Python
python 操作mysql数据中fetchone()和fetchall()方式
May 15 Python
Python代码需要缩进吗
Jul 01 Python
Python包argparse模块常用方法
Jun 04 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的引用原因分析
2012/09/06 PHP
PHP屏蔽蜘蛛访问代码及常用搜索引擎的HTTP_USER_AGENT
2013/03/06 PHP
PHP中的一些常用函数收集
2015/05/26 PHP
LaravelS通过Swoole加速Laravel/Lumen详解
2018/03/02 PHP
Laravel 框架返回状态拦截代码
2019/10/18 PHP
JavaScript this 深入理解
2009/07/30 Javascript
DLL+ ActiveX控件+WEB页面调用例子
2010/08/07 Javascript
Jquery中的$.each获取各种返回类型数据的使用方法
2015/05/03 Javascript
CSS+JS实现点击文字弹出定时自动关闭DIV层菜单的方法
2015/05/12 Javascript
浅析AMD CMD CommonJS规范--javascript模块化加载学习心得总结
2016/03/16 Javascript
jQuery插件autocomplete使用详解
2017/02/04 Javascript
详解tween.js的使用教程
2017/09/14 Javascript
利用百度地图API获取当前位置信息的实例
2017/11/06 Javascript
详解使用VueJS开发项目中的兼容问题
2018/08/02 Javascript
Node.js Koa2使用JWT进行鉴权的方法示例
2018/08/17 Javascript
详解webpack 热更新优化
2018/09/13 Javascript
vue.js实现的幻灯片功能示例
2019/01/18 Javascript
如何使用JavaScript实现栈与队列
2019/06/24 Javascript
layer的prompt弹出框,点击回车,触发确定事件的方法
2019/09/06 Javascript
python中文编码问题小结
2014/09/28 Python
更改Python命令行交互提示符的方法
2015/01/14 Python
python使用urllib2提交http post请求的方法
2015/05/26 Python
Python3中类、模块、错误与异常、文件的简易教程
2017/11/20 Python
Python调用服务接口的实例
2019/01/03 Python
python使用协程实现并发操作的方法详解
2019/12/27 Python
CSS3弹性盒模型开发笔记(三)
2016/04/26 HTML / CSS
澳大利亚的奢侈品牌:Oroton
2016/08/26 全球购物
GAP欧盟网上商店:GAP EU
2016/09/13 全球购物
美国男士和女士奢侈品折扣手表购物网站:Certified Watch Store
2018/06/13 全球购物
俄罗斯汽车零件和配件在线商店:CarvilleShop
2019/11/29 全球购物
产品销售计划书
2014/05/04 职场文书
党员个人对照检查材料思想汇报
2014/09/16 职场文书
学习退步检讨书
2014/09/28 职场文书
财务稽核岗位职责
2015/04/13 职场文书
超级实用!五步法则,教你写好年终工作总结
2019/12/05 职场文书
python plt.plot bar 如何设置绘图尺寸大小
2021/06/01 Python