python深拷贝浅拷贝,python中的深拷贝和浅拷贝
仅供参考,转载请注明出处。
深抄,浅抄1。灯光副本灯光副本是对象的顶层副本。
通俗的理解就是:抄引用,不抄内容。
浅抄示意图
用ipython3写几个例子看看:
在[1]中:a=[1,2,3,4]
在[2]中:b=a
在[3]中
输出[3]: [1,2,3,4]
在[4]: b
输出[4]: [1,2,3,4]
In [5]: id(a) #检查变量a的内存地址。
Out[5]: 140490275823112
In [6]: id(b) #检查变量b的内存地址。
Out[6]: 140490275823112
在[7]:导入副本
在[8]中:
in[8]:c=copy . copy(a)# copy a with copy
在[9]中:c
输出[9]: [1,2,3,4]
In [10]: id(c) #检查变量c的内存地址。
Out[10]: 140490271207112
在[11]中:从上面的例子来看,在b=a和c=copy.copy(a)两种方式中,b和a的内存地址是14049027582312,而c的内存地址是140490271207112。c已经指向了另一个内存地址。
解释:b=a符合光抄法则。
思考:既然浅拷贝都指向同一个内存地址,那么如果修改一个变量,那么另一个变量指向的所有值会一起修改吗?
#首先,在最后一步之后检查三个变量a b c的值。
在[11]: c
输出[11]: [1,2,3,4]
在[12]中
Out[12]: [1,2,3,4]
在[13]: b
输出[13]: [1,2,3,4]
#因为A和B的两个变量都指向同一个内存地址,所以在B的列表中添加一个5,并检查变量的修改。
在[14]中:b .追加(5)
在[15]: b
Out[15]: [1,2,3,4,5]
在[16]中
Out[16]: [1,2,3,4,5]
在[17]: c
Out[17]: [1,2,3,4]
#从上面三个变量可以看出,变量A和B是同时被修改的,而C因为内存地址不同,所以没有被修改。
#然后修改变量C,在列表中添加一个数字6。当然不会影响变量A和b,实践看看。
在[18]中:c .追加(6)
在[19]: c
Out[19]: [1,2,3,4,6]
在[20]中
Out[20]: [1,2,3,4,5]
在[21]: b
Out[21]: [1,2,3,4,5]
在[22]中:
注:其实上面的理解对于轻抄是有一定偏差的。虽然b=a确实是一种轻拷贝,但是轻拷贝c=copy.copy(a)是另一种轻拷贝。那么为什么记忆不一样呢?
其实浅拷贝只是拷贝最顶层的数据,还会生成一个新的变量,此时内存会有所不同。这里有另一个例子来说明:
在[22]中:d=[a,b] #先设置变量D将A和b相加。
在[23]中:e=copy.copy(d) #用e轻拷贝d,此时会生成一个新的内存变量e。
在[24]中:id(d) #检查d的变量存储器
Out[24]: 140490271478024
在[25]: id(e) #查E的变量内存,确实和d不一样,但是E复制的A和B的地址呢?
Out[25]: 140490295815880
In [26]: id(d[0]) #首先检查变量A在d中的列表地址。
Out[26]: 140490275823112
In [27]: id(e[0]) #看E中变量A的列表内存地址,其实是一样的。
Out[27]: 140490275823112
在[28]: id(d[1])
Out[28]: 140490275823112
在[29]: id(e[1])
Out[29]: 140490275823112
在[30]中:从上面的结果来看,C和D的变量内存地址是不同的,但是A和B在C和D中的内存地址是相同的。
那么是不是A修改了,那么C和D也会同时修改呢?
在[30]中:a .附加(7)
在[31]中
Out[31]: [1,2,3,4,5,7]
在[32]: b
Out[32]: [1,2,3,4,5,7]
在[33]: e[0]
Out[33]: [1,2,3,4,5,7]
在[34]: d[0]
Out[34]: [1,2,3,4,5,7]
在[35]:答案会同时修改,因为内存地址都是一致的。
下面是一个理解图:
2.深层拷贝
深层副本是对象所有级别的副本(递归)。
在[35]中:a=[11,22]
In [36]: b=copy.deepcopy(a) #深度复制a指向的列表。
在[37]中
出[37]: [11,22]
在[38]: b
出[38]: [11,22]
在[39]: id(a) #检查a的存储器地址。
Out[39]: 140490295123912
在[40]: id(b) #看B的内存地址,可以看到它和变量a不一致。
Out[40]: 140490295270088
在[43]中:a.append(33) #那么给A加一个变量33肯定不会影响b .执行一下看看。
在[44]中
Out[44]: [11,22,33]
在[45]中
出[45]: [11,22]
在[46]:但是从这个例子来看,深度复制并没有什么特别之处。
进一步了解深度复制
从前面浅拷贝的例子,我们来看看使用深拷贝时有什么变化。
在[46]中:a=[1,2,3,4]
在[47]中:b=a
在[48]中:d=[a,b]
在[49]中:e=copy.deepcopy(d) #用e对d进行深度复制,然后d中的所有变量将被深度递归。
在[50]中:id(d) #检查变量d的内存。
Out[50]: 140490296753416
在[51]: id(e) #看变量E的内存,可以看到E和D的内存地址是不一样的。
Out[51]: 140490295210696
#上例中,d[0]的内存地址和c[0]的是一样的,但是这里的深度复制是不一样的。
在[52]: id(d[0])
Out[52]: 140490271451720
在[53]: id(e[0])
Out[53]: 140490294520008
在[54]中:id(e[1])
Out[54]: 140490294520008
在[55]: id(d[1])
Out[55]: 140490271451720
#从上面的打印来看,深度拷贝后,E和D的所有内存变量都不一样了。
在[56]中:d
Out[56]: [[1,2,3,4],[1,2,3,4]]
在[57]: e
Out[57]: [[1,2,3,4],[1,2,3,4]]
在[58]中:a .追加(5)
在[59]: d
Out[59]: [[1,2,3,4,5],[1,2,3,4,5]]
在[60]: e
Out[60]: [[1,2,3,4],[1,2,3,4]]
在[61]:3。其他复制方式
分段表达式d=c[:]可以赋值一个序列。
在[1]中:a=[11,22]
在[2]中:b=[33,44]
在[3]中:c=[a,b]
在[4]: d=c[:] #分段表达式中
在[5]中:c
Out[5]: [[11,22],[33,44]]
在[6]: d
Out[6]: [[11,22],[33,44]]
#使用切片表达式传递值后查看变量内存,可以看出C和D是不同的。
在[7]中:id(c)
Out[7]: 140089352809416
在[8]中:id(d)
Out[8]: 140089352792904
#然后再看c[0]和d[0]的内存变量。从结果来看,是一样的。
In [9]: id(c[0])
Out[9]: 140089352742024
在[10]: id(d[0])
Out[10]: 140089352742024
#所以既然内存地址都是一样的,那就给A加一个变量33,看看值是不是同时被修改了。
在[11]中
Out[11]: [11,22]
在[12]中:a .追加(33)
在[13]中
Out[13]: [11,22,33]
在[14]: c
Out[14]: [[11,22,33],[33,44]]
在[15]: d
Out[15]: [[11,22,33],[33,44]]
在[16]:从上面的结果来看,切片表达式是一种浅拷贝。
字典的复制方法可以复制字典。
在[16]:导入副本
#创建字典
在[17]: d=dict(姓名=张三,年龄=27)
#将字典D复制到co
在[18]中:co=d.copy()
#检查d和co的值。
在[19]: d
[19]: {“姓名”:“张三”,“年龄”:27}
在[20]: co
[20]: { 姓名:张三,年龄:27}
#看看D和co的记忆值,可以看出它们是不一样的。
在[21]中:id(d)
Out[21]: 140089352402120
在[22]中:id(c)
Out[22]: 140089352809416
在[23]中:
#然后直接为d设置新的字典内容。
In [23]: d=dict(姓名=张三,年龄=27,儿童_年龄=[11,22])
在[24]: d
Out[24]: { 姓名:张三,年龄:27,儿童_年龄:[11,22]}
在[26]: co
[26]: { 姓名:张三,年龄:27}
#再次将D复制到co
在[27]: co=d.copy()
在[28]: co
Out[28]: { 姓名:张三,年龄:27,儿童_年龄:[11,22]}
#将列表编号9添加到字典中的children_ages
在[29]: d[儿童_年龄]。追加(9)
#你可以看到co也跟着变了,说明children_ages的内存地址是一致的。
在[30]: d
Out[30]: { 姓名:张三,年龄:27,儿童_年龄:[11,22,9]}
在[31]: co
Out[31]: { 姓名:张三,年龄:27,儿童_年龄:[11,22,9]}
在[32]中:
In [32]: id(d[children_ages])
Out[32]: 140089267051336
In [33]: id(co[children_ages])
Out[33]: 140089267051336
在[34]中:
4.注意轻拷贝和不可变拷贝的区别。
对于可变类型,将进行浅层复制。Copy对于不可变类型,不会复制,只指向# copy列表可变类型。
在[34]中:a=[11,22,33]
在[35]中:b=copy.copy(a)
在[36]中:id(a)
Out[36]: 140089256561608
在[37]中:id(b)
Out[37]: 140089352086664
在[38]中:a .追加(44)
在[39]中
Out[39]: [11,22,33,44]
在[40]: b
Out[40]: [11,22,33]
在[41]中:
#再用元祖来论证。可以看到复制的两个变量的内存地址是一致的。
在[41]中:a=(11,22,33)
在[42]中:b=copy.copy(a)
[43]同上(a)
Out[43]: 140089283270624
在[44]中:id(b)
Out[44]: 140089283270624
在[45]中:
copy.copy和copy.deepcopy的区别
复制复制
在[45]中:a=[11,22]
#用元组把A括起来,那么后续的内存地址就不会改变。
在[46]中:b=(a,)
在[47]中
Out[47]: ([11,22],)
在[48]中:c=[b,]
在[49]: c
Out[49]: [([11,22],)]
#用D轻拷贝C,那么C中的元组内存地址会发生变化吗?
在[50]中:d=copy.copy(c)
在[51]: d
Out[51]: [([11,22],)]
#先检查变量C和D的内存地址,发现不一样,正常。
[52]同上(c)
Out[52]: 140089267047816
在[53]中:id(d)
Out[53]: 140089352213384
#看C和D的元组内存地址,发现是一样的,所以是原变量a的地址。
在[57]: id(c[0])
Out[57]: 140089352719216
在[58]: id(d[0])
Out[58]: 140089352719216
#给变量A加33,那么C和D将同时增加,如下所示:
在[54]中:a .追加(33)
在[55]: c
Out[55]: [([11,22,33],)]
在[56]中:d
Out[56]: [([11,22,33],)]
让我们来看看完成使用可变列表的例子。
在[60]中:a=[11,22]
在[61]中:b=[a]
在[62]中
Out[62]: [[11,22]]
在[63]中:c=[b]
在[64]: c
Out[64]: [[[11,22]]]
在[65]中:d=copy.copy(c)
在[66]中:d
Out[66]: [[[11,22]]]
在[67]中:id(d)
Out[67]: 140089282147016
在[68]中:id(c)
Out[68]: 140089352347848
在[69]: id(d[0])
Out[69]: 140089356847432
在[70]: id(c[0])
Out[70]: 140089356847432
在[71]: id(d[0][0])
Out[71]: 140089282502536
在[72]: id(c[0][0])
Out[72]: 140089282502536
在[73]: c
Out[73]: [[[11,22]]]
在[74]: d
Out[74]: [[[11,22]]]
在[75]中
出[75]: [11,22]
在[76]中:a .追加(33)
在[77]: c
Out[77]: [[[11,22,33]]]
在[78]: d
Out[78]: [[[11,22,33]]]
在[79]中
Out[79]: [11,22,33]
在[80]:从上面的操作来看,只要是浅拷贝的变量,所有的内部数据都会指向同一个内存地址。
副本.深层副本
关注微信微信官方账号,回复【数据】,Python,PHP,JAVA,web,即可获取Python,PHP,JAVA,前端等视频数据。
来自海洋的渔夫原创作品,
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。