浅谈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实现巡检系统(solaris)示例
Apr 02 Python
python判断、获取一张图片主色调的2个实例
Apr 10 Python
在阿里云服务器上配置CentOS+Nginx+Python+Flask环境
Jun 18 Python
Python使用sorted排序的方法小结
Jul 28 Python
使用Python写一个小游戏
Apr 02 Python
Python使用numpy模块创建数组操作示例
Jun 20 Python
pandas去除重复列的实现方法
Jan 29 Python
深入解析python中的实例方法、类方法和静态方法
Mar 11 Python
基于python实现微信好友数据分析(简单)
Feb 16 Python
Python requests设置代理的方法步骤
Feb 23 Python
Django认证系统user对象实现过程解析
Mar 02 Python
python定时截屏实现
Nov 02 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
动画 《Pokemon Sword·Shield》系列WEB动画《薄明之翼》第2话声优阵容公开!
2020/03/06 日漫
PHP学习笔记(一):基本语法之标记、空白、和注释
2015/04/17 PHP
php格式化时间戳
2016/12/17 PHP
小程序微信退款功能实现方法详解【基于thinkPHP】
2019/05/05 PHP
如何通过Apache在本地配置多个虚拟主机
2020/07/29 PHP
javascript中巧用“闭包”实现程序的暂停执行功能
2007/04/04 Javascript
用JAVASCRIPT如何给<textarea></textarea>赋值
2007/04/20 Javascript
jQuery 页面 Mask实现代码
2010/01/09 Javascript
js charAt的使用示例
2014/02/18 Javascript
js获取IP地址的方法小结
2014/07/01 Javascript
深入剖析javascript中的exec与match方法
2016/05/18 Javascript
jQuery数据检索中根据关键字快速定位GridView指定行的实现方法
2016/06/08 Javascript
如何用js实现鼠标向上滚动时浮动导航
2016/07/18 Javascript
微信小程序 解决请求服务器手机预览请求不到数据的方法
2017/01/04 Javascript
jQuery EasyUI Panel面板组件使用详解
2017/02/28 Javascript
vue页面使用阿里oss上传功能的实例(一)
2017/08/09 Javascript
vue与vue-i18n结合实现后台数据的多语言切换方法
2018/03/08 Javascript
vue项目中jsonp跨域获取qq音乐首页推荐问题
2018/05/30 Javascript
Vue.js子组件向父组件通信的方法实例代码详解
2018/12/10 Javascript
JS中使用react-tooltip插件实现鼠标悬浮显示框
2019/05/15 Javascript
深入浅析vue全局环境变量和模式
2020/04/28 Javascript
Vue 列表页带参数进详情页的操作(router-link)
2020/11/13 Javascript
[57:09]DOTA2-DPC中国联赛 正赛 Phoenix vs Dynasty BO3 第一场 1月26日
2021/03/11 DOTA
Python实现将MySQL数据库表中的数据导出生成csv格式文件的方法
2018/01/11 Python
基于Python 装饰器装饰类中的方法实例
2018/04/21 Python
python命令行参数用法实例分析
2019/06/25 Python
Django分页功能的实现代码详解
2019/07/29 Python
python对指定字符串逆序的6种方法(小结)
2020/04/02 Python
无惧面试,带你搞懂python 装饰器
2020/08/17 Python
css3给背景图片加颜色遮罩的方法
2019/11/05 HTML / CSS
大专生自荐信
2013/10/04 职场文书
自荐信的五个重要部分
2013/10/29 职场文书
大学生创业策划书
2014/02/02 职场文书
广告艺术设计专业自荐书
2014/07/08 职场文书
护士优质服务演讲稿
2014/08/26 职场文书
党风廉政建设心得体会
2019/05/21 职场文书