python 中 深拷贝和浅拷贝的理解
在总结 python 对象和引用的时候,想到其实 对于python的深拷贝和浅拷贝也可以很好对其的进行理解。
在python中,对象的赋值的其实就是对象的引用。也就是说,当创建一个对象,然后赋给另外一个变量之后,实际上只是拷贝了这个对象的引用。
我们先用 利用切片操作和工厂方法list方法 来阐述一下浅拷贝。
举个栗子:
Tom = ['Tom', ['age', 10]]
Jack = Tom[:] ……切片操作
June = list(Tom)
接下来查看一下 上述三个变量的引用:
>>> id(Tom)
140466700697824
>>> id(Jack)
140466700700488
>>> id(June)
140466700770192
可以发现,三个变量分别指向了不同的对象。我们再来看一下这三个对象的内容:
>>> Tom
['Tom', ['age', 10]]
>>> Jack
['Tom', ['age', 10]]
>>> June
['Tom', ['age', 10]]
显然这三个对象的内容会是一样的,因为通过上面的 切片操作 以及 工厂函数list 对Tom引用的对象进行了拷贝。接下来再进行进一步验证:
我们对 Jack 和 June 引用的对象进行修改:
>>> Jack[0] = 'Jack'
>>> Jack
['Jack', ['age', 10]]
>>> June[0] = 'June'
>>> June
['June', ['age', 10]]
现在我们打算对Jack的年龄进行修改:
>>> Jack[1][1] = 20
>>> Jack
['Jack', ['age', 20]]
可以看到Jack年龄变为了20; 让我们接下来看一下Tom, June的年龄:
>>> print Tom, Jack, June
['Tom', ['age', 20]] ['Jack', ['age', 20]] ['June', ['age', 20]]
奇怪的事情发生了,我们仅仅是修改了 Jack的年龄, 但是Tom 和 June 的年龄跟着改变了, 这是为什么呢?
这个就涉及到了python中浅拷贝的奥秘:
我们先来看一下上面 Tom, Jack, June中内部元素的 内存地址:
>>> for x in Tom:
... print id(x)
...
140704715293600 --> 'Tom'
140704715147816 --> ['age', 20]
>>> for x in Jack:
... print id(x)
...
140704715286256 --> 'Jack'
140704715147816 --> ['age', 20]
>>> for x in June:
... print id(x)
...
140704715286352 -->'June'
140704715147816 -->['age', 20]
仔细观察可以看到,Tom, Jack, June 三个变量的 岁数元素['age', 20] 指向同一个 对象; 那为什么他们的 名字元素 分别指向不同的对象。这是因为,在python中的分为 可变数据对象(列表,字典) 和 不可变数据对象(整型,字符串,浮点型,元祖)。 正是因为这个原因,他们的 名字元素 为字符串,为不可变数据对象,因此开始为 Jack 和 June 重新命名的时候,实际上内存中已经创建了 Jack 和 June对象。而 岁数元素 是 可变数据对象,所以并不会在内存中创建新的对象,Tom,Jack,June的岁数元素都引用同一个对象,导致修改其中一个会让另外俩个的年龄跟着变化。
这个就是python的浅拷贝,其仅仅是拷贝了 一个整体的对象(应该说一个对象最外面的那一层),而对于对象里面包含的元素不会进行拷贝。
接下来,我们 利用copy中的deepcopy方法 来阐述一下 深拷贝:
还是用上面那个栗子:
为了让 Tom, Jack, June之间互不影响,我们用deepcopy方法对Tom进行拷贝生成 Jack 和 June:
>>> Tom = ['Tom', ['age', 10]]
>>> import copy
>>> Jack = copy.deepcopy(Tom)
>>> June = copy.deepcopy(Tom)
>>> Jack
['Tom', ['age', 10]]
>>> June
['Tom', ['age', 10]]
>>> Tom
['Tom', ['age', 10]]
让我们看一下Tom, Jack, June分别指向的内存地址:
>>> print id(Tom), id(June), id(Jack)
140707738759392 140707738799280 140707738797192
三个内存地址不同,然后我们接着改变Jack 和 June的名字,并查看修改后它们的内部元素所指向的内存地址:
>>> Jack[0] = 'Jack'
>>> June[0] = 'June'
>>> Tom
['Tom', ['age', 10]]
>>> Jack
['Jack', ['age', 10]]
>>> June
['June', ['age', 10]]
>>> for x in Tom:
... print id(x)
...
140707738882976 --> 'Tom'
140707738737192 --> ['age', 10]
>>> for x in Jack:
... print id(x)
...
140707738875584 --> 'Jack'
140707738910016 --> ['age', 10]
>>> for x in June:
... print id(x)
...
140707738876640 -->'June'
140707738910160 --> ['age', 10]
可以清楚的看到,他们的内部元素也指向了不同的对象,说明通过deepcopy方法,对元素进行了彻底的拷贝(包括内部元素)。
最后总结一下思路:
思路一:利用切片操作和工厂方法list方法拷贝就叫浅拷贝,只是拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。
思路二:利用copy中的deepcopy方法进行拷贝就叫做深拷贝,外围和内部元素都进行了拷贝对象本身,而不是引用。
但是对于数字,字符串和其他原子类型对象等,没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。
python 中 深拷贝和浅拷贝的理解的更多相关文章
- 关于Python中深拷贝与浅拷贝的理解(一)---概念
import copy a = [1, 2, 3, 4, ['a', 'b']] #原始对象 b = a #赋值,传对象的引用 c = copy.copy(a) #对象拷贝,浅拷贝 d = copy. ...
- python中深拷贝和浅拷贝
python中所谓浅拷贝就是对引用的拷贝,所谓深拷贝就是对对象的资源的拷贝. 首先,对赋值操作我们要有以下认识: 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 ). 修改不可变 ...
- Python中深拷贝与浅拷贝的区别
转自:http://blog.csdn.net/u014745194/article/details/70271868 定义: 在Python中对象的赋值其实就是对象的引用.当创建一个对象,把它赋值给 ...
- python中深拷贝与浅拷贝
# 1.浅拷贝(复制东西)a = [11,22,33] # 实际上是浅拷贝# 没有把这个变量的值赋进去,而是把另一个变量的地址拿过去了,就叫浅拷贝.b = a # print(id(a))# prin ...
- python 中深拷贝和浅拷贝的区别
通俗的理解,浅就是外面,深就是里面.浅拷贝的意思就是只拷贝外面的一层,深拷贝就是拷贝的里面的所有. 看两段代码: 元组: #!/usr/bin/env/python # -*-coding:utf-8 ...
- Python中深拷贝与浅拷贝区别
浅拷贝, list值是可变的,str值不可变,只能重新赋值 a=b=c='wjx'print(a,b,c)c= 'jmy'#重新赋值了,所以内存分配了新的地址print(a,b,c)print(id( ...
- python中self cls init的理解
原创文章,未经允许禁止转载! python中self cls init的理解 python中self cls init的理解
- 图解python中赋值、浅拷贝、深拷贝的区别
Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果.下面本文就通过简单的例子介绍一下这些概念之间的差别. 对象赋值 直接看一段代码: will = ...
- python中赋值和浅拷贝与深拷贝
初学编程的小伙伴都会对于深浅拷贝的用法有些疑问,今天我们就结合python变量存储的特性从内存的角度来谈一谈赋值和深浅拷贝~~~ 预备知识一——python的变量及其存储 在详细的了解python中赋 ...
随机推荐
- Oracle导入和导出
导出:EXP userid=<username>/<password>@<service_name> file=<dmpname> e.g.exp sa ...
- 详细安装ss的过程(vultr)
#更新程序yum update -y #安装setuptoolsyum install -y python-setuptools #安装pipeasy_install pip #安装shadowsoc ...
- centos修改hostname以及时间同步
centos修改hostname 方法一: 执行命令:hostname test 则修改hostname为test 方法二: 永久修改hostname vi /etc/sysconfig/networ ...
- httpwebrequest详解【转】
http://blog.csdn.net/sjj2011/article/details/7823392 HttpWebRequest和HttpWebResponse类是用于发送和接收HTTP数据的最 ...
- [VB.NET]调用系统的文件夹选择对话框
以下示例代码展示如何调用系统的文件夹选择对话框: Private Function SelectFolder(ByVal Describe As String, Optional ByVal Show ...
- Spark SQL External Data Sources JDBC官方实现读测试
在最新的master分支上官方提供了Spark JDBC外部数据源的实现,先尝为快. 通过spark-shell测试: import org.apache.spark.sql.SQLContext v ...
- 并发:OPP 响应超并发:OPP 响应超时
用户提交报表,无法正常结束,报表日志中有如下消息: “正在执行请求完成选项... +------------- 1) PUBLISH -------------+ 节点 FIN1 上的请求 29884 ...
- CAS 与.net 集成的 “循环重定向”问题分析
转自:http://www.cnblogs.com/xingshao/archive/2011/09/29/2195746.html 近期的一个项目,项目包含了若干的子系统,因为人员配备的原因,项目会 ...
- 树莓派_360wifi2_佳能MP236打印机
入手树莓派后一直没时间弄,设想用360wifi做无线网卡,也一直不得解,今天成功写下经验 本人刷的是官方系统,版本为3.12.28,首先更新系统内核,参考以下网址: http://groenholdt ...
- JavaScript基础篇
写的不错,转 http://www.cnblogs.com/suoning/p/5656403.html