浅谈Python浅拷贝、深拷贝及引用机制


Posted in Python onDecember 15, 2016

这礼拜碰到一些问题,然后意识到基础知识一段时间没巩固的话,还是有遗忘的部分,还是需要温习,这里做份笔记,记录一下

前续

先简单描述下碰到的题目,要求是写出2个print的结果

浅谈Python浅拷贝、深拷贝及引用机制

可以看到,a指向了一个列表list对象,在Python中,这样的赋值语句,其实内部含义是指a指向这个list所在内存地址,可以看作类似指针的概念。

而b,注意,他是把a对象包裹进一个list,并且乘以5,所以b的样子应该是一个大list,里面元素都是a

而当a对象进行了append操作后,其实,隐含的意思是,内存中的这个list进行了修改,所有对此对象进行引用的对象,都会发生改变

我将a的id打印出来,并且,同时打印b这个对象中所包含的元素a的id,这样可以看到,在b这个list中,每个元素的id,和a是一样的

浅谈Python浅拷贝、深拷贝及引用机制

我们可以看到,a对象的id(内存地址)为10892296,虽然b把a包裹进了新的list,但是,这个元素引用的,还是相同地址的对象,可以用下图来解释

浅谈Python浅拷贝、深拷贝及引用机制

之后,我们对a进行了append操作,由于list是一个可变对象,所以,他的内存地址并没有改变,但是,对于内存中这个地址的引用的所有对象,都会被一同改变可以从上面测试图分割线下半部分看出来.

由此,引出了对Python引用机制和浅复制及深复制的复习

Python的引用机制

引用机制案例1

由上面的例子,我们可以看到,python的引用传递,最终结果是让2个对象都引用内存中同一块区域的内容

所以我们来看一下下面的例子

B通过A,同样引用了id为17446024的地址的内容,2者的id(内存地址)都是一毛一样的

所以,通过A的操作  A[0]=3  或是   A[3].append(6)  ,都会对这块内存中的内容进行修改(因为list是可变对象,所以内存地址并不会改变,这个后面再讲)

这个是最基本的引用案例 (另外说句,由于A和B都指向了同一块内存地址,所以通过B修改的内容,也能反映到A上面去)

浅谈Python浅拷贝、深拷贝及引用机制

引用机制案例2

我们再来看一个案例

看题目貌似是会把元素2替换成本身这个列表,结果也许应该是 A=[1,[1,2,3],3]

但其实并不是!!你可以看到,红框中部分,中间有无限多个嵌套

为什么会这样呢?

其实是因为,A指向的是[1,2,3]这个列表,在这个例子中,只是把A的第2个元素,指向了A对象本身,所以说,只是A的结构发生了变化!但是,A还是指向那个对象

我们可以通过打印A的id来看,他的指向是没有变的!!

浅谈Python浅拷贝、深拷贝及引用机制

来看一下,A的指向并没有变

浅谈Python浅拷贝、深拷贝及引用机制

那如果我们要达到最后输出效果是 [1,[1,2,3],3]的效果,应该如何来操作呢?

这里,我们就要用到浅复制了,用法可以如下

 浅谈Python浅拷贝、深拷贝及引用机制

浅复制和深复制

浅复制

现在,就来说说浅复制和深复制,上面的方法实际上只是进行了浅复制,shallow copy,含义是他是对原来引用的对象进行了复制,但是不再引用同一对象地址

看下面的例子,B通过 B = A[:] 操作,来进行了浅复制,你可以看到,浅复制之后,A和B引用的内存地址已经是不同的了
但是,A和B内部的元素的引用地址,还是相同的,这点要非常注意!是有区别的!!!

A和B的引用内存地址的不同,带来的效果是,你在B上面进行的操作,并不会影响到A。

浅谈Python浅拷贝、深拷贝及引用机制

浅拷贝归纳:

所以浅拷贝,可以归纳为,复制一份引用,新的对象和原来的对象的引用被区分开,但是内部元素的地址引用还是相同的

但是浅复制也会有问题,问题在哪里呢?就是碰到有嵌套的情况,比如下面的情况可以看到,我给B赋值了一份A的浅复制,这样A和B的id(内存地址)就不一样了。

所以,当我修改A[0]=8的时候,B不会被影响到,因为他们A和B两者是独立的引用,但是这里中间有一个嵌套的列表 [4,5,6]
这个[4,5,6]我们可以理解为:A和B还共同引用着,也就是对于A和B的第二个元素来说,他们还是指向同一块内存地址的。

另外要说一句,由于int是不可变类型,所以,把A[0]修改成8之后,他的引用地址就变了!就和B[0]这个元素的引用区分开了。

浅谈Python浅拷贝、深拷贝及引用机制

深复制

那如何面对这样的情况呢?就要用到python模块里面的copy模块了

copy模块有2个功能

1: copy.copy(你要复制的对象) : 这个是浅拷贝,和前面对list进行的 [:] 操作性质是一样的

2: copy.deepcopy(你要复制的对象) : 这个是深拷贝,他除了和浅拷贝一样,会新生成一份对象的引用,另外对于内部的元素,都会新生成引用,以独立分开.

看下面的例子,当你给B赋值一份A的深复制之后,他俩可以说是完全独立开了,无论你修改的是A里面的不可变元素,还是修改A里面嵌套的可变元素,结果都不会影响到B

我的理解是:深复制可以称之为递归拷贝,他会把所有嵌套的可变元素都拷贝一下,然后独立引用出来.

浅谈Python浅拷贝、深拷贝及引用机制

深复制归纳:

深复制的效果,除了和浅复制一样,将对象的引用新生成一份引用之外,内部所有嵌套的元素,他都会帮你一一独立开.

自己画了2张图,以表示浅复制和深复制的效果区别

需要说明的是,虽然浅复制之后,列表内不可变元素的引用地址还是相同的,但是,正因为他们是不可变元素,所以,其中任意不可变元素被改变之后,引用地址都会是新的,而不会影响到原来的引用地址。

 

浅谈Python浅拷贝、深拷贝及引用机制

浅谈Python浅拷贝、深拷贝及引用机制

总结

所以,到这里,浅复制和深复制的机制,基本上理解了。

另外还有特殊情况需要说明

对于不可变类型:int, str, tuple, float 这样的元素来说,没有拷贝这个说法,他们被修改之后,引用地址就是直接改变了,如下面

浅谈Python浅拷贝、深拷贝及引用机制

但是,如果不可变类型内部有嵌套的可变类型的时候,还是可以使用深复制的

浅谈Python浅拷贝、深拷贝及引用机制

另外要提醒一句,平时我们用的最多的直接赋值(或者可以说是直接传递引用)的方法,比如下面的例子

他是将a和b两个可变元素同时指向一个内存地址,所以,任何改变都是波及到a和b的

浅谈Python浅拷贝、深拷贝及引用机制

最后

可变类型:list , set , dict

不可变类型:int, str , float , tuple

浅复制方法:[:] , copy.copy() ,  使用工厂函数(list/dir/set)

深复制方法:copy.deepcopy()

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

Python 相关文章推荐
python socket 超时设置 errno 10054
Jul 01 Python
python操作mongodb根据_id查询数据的实现方法
May 20 Python
python 读写txt文件 json文件的实现方法
Oct 22 Python
Python中super函数的用法
Nov 17 Python
python数字图像处理之骨架提取与分水岭算法
Apr 27 Python
python队列queue模块详解
Apr 27 Python
运行django项目指定IP和端口的方法
May 14 Python
详解Python下ftp上传文件linux服务器
Jun 21 Python
Python3实现的回文数判断及罗马数字转整数算法示例
Mar 27 Python
numpy数组广播的机制
Jul 12 Python
详解python如何引用包package
Jun 07 Python
Python偏函数实现原理及应用
Nov 20 Python
利用python获取某年中每个月的第一天和最后一天
Dec 15 #Python
python中快速进行多个字符替换的方法小结
Dec 15 #Python
Python制作简易注册登录系统
Dec 15 #Python
用yum安装MySQLdb模块的步骤方法
Dec 15 #Python
Python3.6正式版新特性预览
Dec 15 #Python
再谈Python中的字符串与字符编码(推荐)
Dec 14 #Python
Python文件操作,open读写文件,追加文本内容实例
Dec 14 #Python
You might like
如何判断php数组的维度
2013/06/10 PHP
PHP程序中使用adodb连接不同数据库的代码实例
2015/12/19 PHP
将HTML自动转为JS代码
2006/06/26 Javascript
在模板页面的js使用办法
2010/04/01 Javascript
javascript学习笔记(九)javascript中的原型(prototype)及原型链的继承方式
2011/04/12 Javascript
Jquery中对数组的操作代码
2011/08/12 Javascript
Javascript的各种节点操作实例演示代码
2012/06/27 Javascript
JavaScript高级程序设计(第3版)学习笔记7 js函数(上)
2012/10/11 Javascript
Eclipse配置Javascript开发环境图文教程
2015/01/29 Javascript
JavaScript函数基础详解
2017/02/03 Javascript
angula中使用iframe点击后不执行变更检测的问题
2020/05/10 Javascript
[01:09:24]Ti4开幕式
2014/07/19 DOTA
[01:04:05]Mineski vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
Python操作MongoDB数据库的方法示例
2018/01/04 Python
高效使用Python字典的清单
2018/04/04 Python
python3.6使用pickle序列化class的方法
2018/10/22 Python
pycharm创建scrapy项目教程及遇到的坑解析
2019/08/15 Python
python通过nmap扫描在线设备并尝试AAA登录(实例代码)
2019/12/30 Python
从训练好的tensorflow模型中打印训练变量实例
2020/01/20 Python
详解tensorflow之过拟合问题实战
2020/11/01 Python
Django怎么在admin后台注册数据库表
2020/11/14 Python
用CSS3来实现社交分享按钮
2014/11/11 HTML / CSS
css3使用animation属性实现炫酷效果(推荐)
2020/02/04 HTML / CSS
凯蒂·佩里个人女鞋品牌:Katy Perry Collections
2019/04/04 全球购物
给民警的表扬信
2014/01/08 职场文书
玲玲的画教学反思
2014/02/04 职场文书
支行行长竞聘演讲稿
2014/05/15 职场文书
拉拉队口号
2014/06/16 职场文书
公安派出所所长四风问题个人对照检查材料
2014/10/04 职场文书
2014年小学图书室工作总结
2014/12/09 职场文书
2014年酒店年度工作总结
2014/12/10 职场文书
离职感谢信怎么写
2015/01/22 职场文书
酒店客房服务员岗位职责
2015/04/09 职场文书
2016年教师师德师风心得体会
2016/01/12 职场文书
温馨祝福晨语:美丽的一天从我的问候开始
2019/11/28 职场文书
关于PHP数组迭代器的使用方法实例
2021/11/17 PHP