python中copy()与deepcopy()的区别小结


Posted in Python onAugust 03, 2018

前言

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

深复制被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。

浅复制并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。

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]]

可以看到 cop1,也就是 copy 跟着 origin 改变了。而 cop2 ,也就是 deep 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 的标签。

python中copy()与deepcopy()的区别小结

第一种情况:

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

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

python中copy()与deepcopy()的区别小结

第二种情况:

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上面。

概念图如下:

python中copy()与deepcopy()的区别小结

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概念图如下:

python中copy()与deepcopy()的区别小结

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

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python解析xml文件操作实例
Oct 05 Python
简单理解Python中的装饰器
Jul 31 Python
python文件与目录操作实例详解
Feb 22 Python
Python cookbook(数据结构与算法)实现对不原生支持比较操作的对象排序算法示例
Mar 15 Python
对pandas进行数据预处理的实例讲解
Apr 20 Python
Python中使用遍历在列表中添加字典遇到的坑
Feb 27 Python
Python3中_(下划线)和__(双下划线)的用途和区别
Apr 26 Python
Python实现制度转换(货币,温度,长度)
Jul 14 Python
python并发编程 Process对象的其他属性方法join方法详解
Aug 20 Python
python 图片二值化处理(处理后为纯黑白的图片)
Nov 01 Python
OpenCV图片漫画效果的实现示例
Aug 18 Python
python+pytest接口自动化之token关联登录的实现
Apr 06 Python
Python爬取个人微信朋友信息操作示例
Aug 03 #Python
python opencv人脸检测提取及保存方法
Aug 03 #Python
Python爬虫爬取新浪微博内容示例【基于代理IP】
Aug 03 #Python
OpenCV+python手势识别框架和实例讲解
Aug 03 #Python
Windows下将Python文件打包成.EXE可执行文件的方法
Aug 03 #Python
Python测试网络连通性示例【基于ping】
Aug 03 #Python
python版opencv摄像头人脸实时检测方法
Aug 03 #Python
You might like
php while循环得到循环次数
2013/10/26 PHP
curl实现站外采集的方法和技巧
2014/01/31 PHP
常用PHP封装分页工具类
2017/01/14 PHP
PHP pthreads v3下同步处理synchronized用法示例
2020/02/21 PHP
Bookmarklet实现启动jQuery(模仿 云输入法)
2010/09/15 Javascript
Javascript查询DBpedia小应用实例学习
2013/03/07 Javascript
Nodejs学习笔记之Global Objects全局对象
2015/01/13 NodeJs
JavaScript动态添加style节点的方法
2015/06/09 Javascript
jQuery中ajax的load()与post()方法实例详解
2016/01/05 Javascript
AngularJs bootstrap搭载前台框架——js控制部分
2016/09/01 Javascript
bootstrap读书笔记之CSS组件(上)
2016/10/17 Javascript
JS实现鼠标移上去显示图片或微信二维码
2016/12/14 Javascript
js实现手机拍照上传功能
2017/01/17 Javascript
js实现4个方向滚动的球
2017/03/06 Javascript
Angualrjs和bootstrap相结合实现数据表格table
2017/03/30 Javascript
vue2.0 中#$emit,$on的使用详解
2017/06/07 Javascript
微信小程序授权获取用户详细信息openid的实例详解
2017/09/20 Javascript
vue源码中的检测方法的实现
2019/09/26 Javascript
小程序接口的promise化的实现方法
2019/12/11 Javascript
python文件操作相关知识点总结整理
2016/02/22 Python
python实现读取并显示图片的两种方法
2017/01/13 Python
python实现FTP服务器服务的方法
2017/04/11 Python
python基础教程项目二之画幅好画
2018/04/02 Python
python爬虫实例详解
2018/06/19 Python
python MNIST手写识别数据调用API的方法
2018/08/08 Python
wxPython实现文本框基础组件
2019/11/18 Python
Python3监控疫情的完整代码
2020/02/20 Python
Pyinstaller 打包发布经验总结
2020/06/02 Python
使用python实现学生信息管理系统
2021/02/25 Python
css3边框_动力节点Java学院整理
2017/07/11 HTML / CSS
美国渔具店:FishUSA
2019/08/07 全球购物
介绍一下#error预处理
2015/09/25 面试题
Java程序员综合测试题
2014/04/25 面试题
机械电子工程专业求职信
2014/06/22 职场文书
教师工作决心书
2015/02/04 职场文书
springBoot基于webSocket实现扫码登录
2021/06/22 Java/Android