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结合opencv实现人脸检测与跟踪
Jun 08 Python
Python通过future处理并发问题
Oct 17 Python
django站点管理详解
Dec 12 Python
python爬虫面试宝典(常见问题)
Mar 02 Python
python最长回文串算法
Jun 04 Python
django-rest-swagger的优化使用方法
Aug 29 Python
Python实现元素等待代码实例
Nov 11 Python
python__new__内置静态方法使用解析
Jan 07 Python
Python安装whl文件过程图解
Feb 18 Python
解决python 在for循环并且pop数组的时候会跳过某些元素的问题
Dec 11 Python
详解python第三方库的安装、PyInstaller库、random库
Mar 03 Python
Django实现drf搜索过滤和排序过滤
Jun 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 Memcached应用实现代码
2010/02/08 PHP
PHP封装的HttpClient类用法实例
2015/06/17 PHP
php简单备份与还原MySql的方法
2016/05/09 PHP
php 变量引用与变量销毁机制详细介绍
2016/12/05 PHP
jquery1.4 教程二 ajax方法的改进
2010/02/25 Javascript
js两行代码按指定格式输出日期时间
2011/10/21 Javascript
window.addEventListener来解决让一个js事件执行多个函数
2012/12/26 Javascript
Extjs单独定义各组件的实例代码
2013/06/25 Javascript
JS中的二叉树遍历详解
2016/03/18 Javascript
微信小程序 页面跳转和数据传递实例详解
2017/01/19 Javascript
nodejs根据ip数组在百度地图中进行定位
2017/03/06 NodeJs
jQuery插件HighCharts绘制简单2D柱状图效果示例【附demo源码】
2017/03/21 jQuery
详解vue嵌套路由-query传递参数
2017/05/23 Javascript
解决layer.open弹出框不能获取input框的值为空的问题
2019/09/10 Javascript
js中addEventListener()与removeEventListener()用法案例分析
2020/03/02 Javascript
javascript如何使用函数random来实现课堂随机点名方法详解
2020/07/28 Javascript
python每隔N秒运行指定函数的方法
2015/03/16 Python
python中pandas.DataFrame的简单操作方法(创建、索引、增添与删除)
2017/03/12 Python
python处理两种分隔符的数据集方法
2018/12/12 Python
python 的 scapy库,实现网卡收发包的例子
2019/07/23 Python
python selenium登录豆瓣网过程解析
2019/08/10 Python
Pytorch反向求导更新网络参数的方法
2019/08/17 Python
解决Pycharm 包已经下载,但是运行代码提示找不到模块的问题
2019/08/31 Python
python实现XML解析的方法解析
2019/11/16 Python
opencv python如何实现图像二值化
2020/02/03 Python
tensorflow 获取checkpoint中的变量列表实例
2020/02/11 Python
python pptx复制指定页的ppt教程
2020/02/14 Python
Python实现图片查找轮廓、多边形拟合、最小外接矩形代码
2020/07/14 Python
HTML5新增加的功能详解
2016/09/05 HTML / CSS
PUMA官方商城:世界领先的运动品牌之一
2016/11/16 全球购物
《少年王冕》教学反思
2014/04/11 职场文书
医院竞聘演讲稿
2014/05/16 职场文书
北京奥运会主题口号
2014/06/13 职场文书
观看建国大业观后感
2015/06/01 职场文书
烛光里的微笑观后感
2015/06/17 职场文书
linux下导入、导出mysql数据库命令的实现方法
2021/05/26 MySQL