由Python的浅拷贝(shallow copy)和深拷贝(deep copy)引发的思考
首先查看拷贝模块(copy)发现:
>>> help(copy)
Help on module copy:
NAME
copy - Generic (shallow and deep) copying operations.
DESCRIPTION
Interface summary:
import copy
x = copy.copy(y) # make a shallow copy of y
x = copy.deepcopy(y) # make a deep copy of y
For module specific errors, copy.Error is raised.
The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or
class instances).
- A shallow copy constructs a new compound object and then (to the extent possible) inserts *the same objects* into it that the
original contains.
- A deep copy constructs a new compound object and then, recursively, inserts *copies* into it of the objects found in the original.
...(here omitted 10000words)
由以上的信息可知:
1、相同点:都拷贝出了一个新的复合对象;
2、不同点:浅拷贝—— 在拷贝出的新的对象上插入(引用)源list对象的一切;
深拷贝—— 递归地拷贝源list对象中的一切。(彻头彻尾的另立门户)
现在出现了一个新的问题—— 拷贝
在计算机中拷贝一份数据或者拷贝一个变量,意味着系统需分配新的内存用于对拷贝数据的存放。
我们先来讨论一下变量的赋值(变量的数据结构中的内存地址域的拷贝)过程。
首先看一下变量的赋值过程:
- Python 2.6.6 (r266:84292, Aug 18 2016, 15:13:37)
- [GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
- Type "help", "copyright", "credits" or "license" for more information.
- >>> a = 3
- >>> b = a
- >>> id(a)
- 7488264
- >>> id(b)
- 7488264
- >>> a = 4
- >>> id(a)
- 7488240
- >>> id(b) # 咦,b没有随a发生改变
- 7488264
- >>> b
3
要解释这个,必须要了解变量的数据结构。
当向系统申请创建一个变量时,系统先分配一块内存空间,该内存空间用于存储该变量。
变量的数据结构包括2部分:第一部分用于存储变量的名称和变量的数据类型的长度,第二部分用于存储内存地址(即索引)。
当变量未初始化时,第二部分数据为垃圾值;一旦初始化,该部分的值即为初始化值的内存地址。
例如:以上 a = 3, 其过程如下:
首先系统为常量3(int型)分配一块内存大小为4byte的空间存放常量3;然后将常量3的内存地址存储于变量a的第二部分。这样就完成了变量a的赋值过程。
b = a时,同样系统先分配一块内存空间存放变量b, 之后系统将a中的第二部分数据拷贝到b中的第二部分。
而id()的返回值正是变量的第二部分数据(内存地址)。
所以当执行a时,是根据第二部分的数据(内存地址)获取该内存的值。
当a = 4 时,变量a第二部分的数据即为常量4的存储地址,因此id(a)发生改变,而id(b)保持不变。
如下图:
回到浅拷贝和深拷贝的议题:
浅拷贝—— 在拷贝出的新的对象上插入(引用)源list对象的一切;
深拷贝—— 递归地拷贝源list对象中的一切。(彻头彻尾的另立门户)。
浅拷贝的实例:
- #!/usr/bin/python # Python2
- #
- import copy
- will = ["Will", 28, ["Python", "C#", "JavaScript"]]
- wilber = copy.copy(will)
- print id(will) #
- print will # ['Will', 28, ['Python', 'C#', 'JavaScript']]
- print [id(ele) for ele in will] # [140337318374208, 13394096, 140337318282160]
- print '============================'
- print id(will[2]) #
- print id(will[2][0]) #
- print id(wilber[2][0]) #
- print id(wilber) #
- print wilber # ['Will', 28, ['Python', 'C#', 'JavaScript']]
- print [id(ele) for ele in wilber] # [140337318374208, 13394096, 140337318282160]
- will[0] = "Wilber"
- will[2].append("CSS")
- print id(will) #
- print will # ['Wilber', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
- print [id(ele) for ele in will] # [140337318374448, 13394096, 140337318282160]
- print id(wilber) #
- print wilber # ['Will', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
- print [id(ele) for ele in wilber] # [140337318374208, 13394096, 140337318282160]
浅拷贝只是生成一个新的对象,数据结构以及索引关系未变。
浅拷贝时,列表will与wilber由系统分配不同的地址,系统将列表will的第一层进行拷贝即:will[0], will[1], will[2]拷贝,故wilber[0]与will[0],wilber[1]与will[1],wilber[2]与will[2],指向相同的内存地址。
如下图所示:
深拷贝实例:
- #!/usr/bin/python
- #
- import copy
- will = ["Will", 28, ["Python", "C#", "JavaScript"]]
- wilber = copy.deepcopy(will)
- print id(will) # 139899797283040
- print will # ['Will', 28, ['Python', 'C#', 'JavaScript']]
- print [id(ele) for ele in will] # [139899797338992, 11432112, 139899797246896]
- print '============='
- print id(will[2]) #
- print id(wilber[2]) #
- print id(will[2][0]) #
- print id(wilber[2][0]) #
- print id(wilber[2][1]) #
- print id(wilber) #
- print wilber # ['Will', 28, ['Python', 'C#', 'JavaScript']]
- print [id(ele) for ele in wilber] # [139899797338992, 11432112, 139899797351024]
- will[0] = "Wilber"
- will[2].append("CSS")
- print id(will) #
- print will # ['Wilber', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
- print [id(ele) for ele in will] # [139899797339280, 11432112, 139899797246896]
- print id(wilber) #
- print wilber # ['Will', 28, ['Python', 'C#', 'JavaScript']]
- print [id(ele) for ele in wilber] # [139899797338992, 11432112, 139899797351024]
深拷贝会递归(逐层)拷贝list的数据结构。
深拷贝时,系统将列表will逐层进行拷贝即:列表will与wilbe,will[2]与wilber[2]由系统分配不同的地址,will[0], will[1], will[2],will[2][0], will[2][1], will[2][2]拷贝;
故wilber[0]与will[0],wilber[1]与will[1], will[2][0]与wilber[2][0], will[2][1]与wilber[2][0], will[2][2]与wilber[2][2],指向相同的内存地址。
附注-list之间的赋值代码:
- #!/usr/bin/python
- #
- will = ["Will", 28, ["Python", "C#", "JavaScript"]]
- wilber = will
- print id(will)
- print will
- print [id(ele) for ele in will]
- print id(wilber)
- print wilber
- print [id(ele) for ele in wilber]
- will[0] = "Wilber"
- will[2].append("CSS")
- print id(will)
- print will
- print [id(ele) for ele in will] # 发现操作的是同一对象
- print id(wilber)
- print wilber
- print [id(ele) for ele in wilber]
由Python的浅拷贝(shallow copy)和深拷贝(deep copy)引发的思考的更多相关文章
- 浅拷贝(Shallow Copy) VS 深拷贝(Deep Copy)
首先,深拷贝和浅拷贝针对的是对象类型(对象,数组,函数) 浅拷贝指的是只是拷贝了对象的引用地址,彼此之间高耦合,一个改变,另一个可能也随之改变: 深拷贝是指只是完整的将变量的值拷贝过来,是一个新的对象 ...
- [置顶] operator overloading(操作符重载,运算符重载)运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy)
operator overloading(操作符重载,运算符重载) 所谓重载就是重新赋予新的意义,之前我们已经学过函数重载,函数重载的要求是函数名相同,函数的参数列表不同(个数或者参数类型).操作符重 ...
- copy&mutableCopy 浅拷贝(shallow copy)深拷贝 (deep copy)
写在前面 其实看了这么多,总结一个结论: 拷贝的初衷的目的就是为了:修改原来的对象不能影响到拷贝出来得对象 && 修改拷贝出来的对象也不能影响到原来的对象 所以,如果原来对象就是imm ...
- angular.extend深拷贝(deep copy)
在用到angular.extend的时候,正好碰到一个对象,是层层嵌套的Array, 结果发现只能extend第一层,查阅官文档,确实不支持deep copy: Note: Keep in mind ...
- [置顶] 运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy),三大件(bigthree problem)
一般的我们喜欢这样对对象赋值: Person p1;Person p2=p1; classT object(another_object), or A a(b); classT object = ...
- 运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy),三大件(bigthree problem)
一般的我们喜欢这样对对象赋值: Person p1;Person p2=p1; classT object(another_object), or A a(b); classT object = ...
- javascript 求最大前5个数; 对象 深拷贝 deep copy
* 用数组 function getTopN(a, n) { function _cloneArray(aa) { var n = aa.length, a = new Array(n); for ( ...
- Python 列表浅拷贝与深拷贝
浅拷贝 shallow copy 和深拷贝 deep copy list.copy() 浅拷贝:复制此列表(只复制一层,不会复制深层对象) 等同于 L[:] 举例: 浅拷贝: a = [1.1, 2. ...
- python deep copy and shallow copy
Python中对于对象的赋值都是引用,而不是拷贝对象(Assignment statements in Python do not copy objects, they create bindings ...
随机推荐
- IIS应用池保持激活工具开发
之前的 开源一个定时任务调度器 webscheduler 中涉及的定时调用应用已经开始在正式环境中启用,发布到IIS下进行测试,发现一旦应用长时间没有访问(大约半个多小时)就会引发 Applicati ...
- 关于导入excel报错的处理(xls,xlsx)
关于导入excel报错的处理(xls,xlsx) 最近在做一个将excel导入到dataGriview中的小功能在做的过程中遇到以下问题: 链接excel的链接串是这样写的 string strCon ...
- 关于Confusion Matrix
from sklearn.metrics import confusion_matrixy_true = [2, 0, 2, 2, 0, 1]y_pred = [0, 0, 2, 2, 0, 2]pr ...
- JAVAWeb SSH框架 利用POI 导出EXCEL,弹出保存框
导入包这一些不多说,直接贴出关键代码,JSP只要点一个Action链接就行. poi包我是用:poi-3.11-20141221.jar 亲测有效: 效果: Action 类代码: private I ...
- [hdu1712]ACboy needs your help分组背包
题意:一共$m$天,$n$门课程,每门课程花费$i$天得到$j$的价值,求最后获得的最大价值 解题关键:分组背包练习,注意循环的顺序不能颠倒 伪代码: $for$ 所有的组$k$ $for{\rm ...
- mongodb教程国外
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
- ASP.NET Core 身份认证 (Identity、Authentication)
Authentication和Authorization 每每说到身份验证.认证的时候,总不免说提及一下这2个词.他们的看起来非常的相似,但实际上他们是不一样的. Authentication想要说明 ...
- 机器学习--近邻成分分析(NCA)算法 和 度量学习
1.近邻成分分析(NCA)算法 以上内容转载自:http://blog.csdn.net/chlele0105/article/details/13006443 2.度量学习 在机器学习中,对高维数据 ...
- ASP.NET jquery 获取服务器控件ID
一般方法: jQuery("#txtUserName").val(); 如果页面加载了母版页或者自定义控件:该页面的ID有可能会被篡改(可能是因为避免控件ID冲突的机制),因此强烈 ...
- asp.net 防止二次提交 以及UseSubmitBehavior属性-转
页面上有一个按钮,点击之后提交表单,如果什么都不管的话,用户可以在服务器响应完成之前再次点击,这样就出现了二次提交,后果可大可小. 那么我们应该防止二次点击,就要在用户点第一次之后马上Disable这 ...