浅谈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 Sleep休眠函数使用简单实例
Feb 02 Python
Python实现将DOC文档转换为PDF的方法
Jul 25 Python
Python正则获取、过滤或者替换HTML标签的方法
Jan 28 Python
分享给Python新手们的几道简单练习题
Sep 21 Python
python实现多线程行情抓取工具的方法
Feb 28 Python
Python运维之获取系统CPU信息的实现方法
Jun 11 Python
pandas去重复行并分类汇总的实现方法
Jan 29 Python
python opencv将表格图片按照表格框线分割和识别
Oct 30 Python
基于python实现图片转字符画代码实例
Sep 04 Python
Python3读写ini配置文件的示例
Nov 06 Python
python Timer 类使用介绍
Dec 28 Python
Elasticsearch 基本查询和组合查询
Apr 19 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
Http 1.1 Etag 与 Last-Modified提高php效率
2008/01/10 PHP
PHP读取ACCESS数据到MYSQL的代码
2011/05/11 PHP
zf框架的Filter过滤器使用示例
2014/03/13 PHP
php匹配字符中链接地址的方法
2014/12/22 PHP
PHP7新特性之抽象语法树(AST)带来的变化详解
2018/07/17 PHP
laravel中的一些简单实用功能
2018/11/03 PHP
简略的前端架构心得&&基于editor为例子的编码小技巧
2010/11/25 Javascript
Javascript 面向对象编程(coolshell)
2012/03/18 Javascript
jsPDF导出pdf示例
2014/05/02 Javascript
js强制把网址设为默认首页
2015/09/29 Javascript
JavaScript实现斗地主游戏的思路
2016/02/29 Javascript
js删除局部变量的实现方法
2016/06/25 Javascript
jquery网页日历显示控件calendar3.1使用详解
2016/11/24 Javascript
Vue2.0 http请求以及loading展示实例
2018/03/06 Javascript
解决vue-quill-editor上传内容由于图片是base64的导致字符太长的问题
2018/08/20 Javascript
使用FormData实现上传多个文件
2018/12/04 Javascript
jQuery实现每日秒杀商品倒计时功能
2019/09/06 jQuery
浅谈JavaScript中等号、双等号、 三等号的区别
2020/08/06 Javascript
vue中实现点击变成全屏的多种方法
2020/09/27 Javascript
[03:59]DOTA2英雄梦之声_第07期_水晶室女
2014/06/23 DOTA
Python从零开始创建区块链
2018/03/06 Python
tensorflow获取变量维度信息
2018/03/10 Python
超简单使用Python换脸实例
2019/03/27 Python
django 信号调度机制详解
2019/07/19 Python
IE下实现类似CSS3 text-shadow文字阴影的几种方法
2011/05/11 HTML / CSS
萨克斯第五大道精品百货店: Saks Fifth Avenue
2017/04/28 全球购物
应付会计岗位职责
2013/12/12 职场文书
医药个人求职信范文
2014/01/29 职场文书
婚纱摄影师求职信范文
2014/04/17 职场文书
党的群众路线教育实践活动对照检查材料
2014/09/22 职场文书
委托书格式要求
2015/01/28 职场文书
刑事附带民事起诉状
2015/05/19 职场文书
大学生就业指导课心得体会
2016/01/15 职场文书
个人道歉信大全
2019/04/11 职场文书
oracle数据库去除重复数据
2022/05/20 Oracle
springcloud整合seata
2022/05/20 Java/Android