Python-copy()与deepcopy()区别详解
最近在实习,boss给布置了一个python的小任务,学习过程中发现copy()和deepcopy()这对好基友实在是有点过分,搞的博主就有点傻傻分不清啊,但是呢本着一探到底的精神,还是要查资料搞清楚这对好基友的区别。
其实呢,copy()与deepcopy()之间的区分必须要涉及到python对于数据的存储方式。
首先直接上结论:
—–我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。
—–而浅复制并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。这就和我们寻常意义上的复制有所不同了。
对于简单的object,用shallowcopy和deepcopy没区别
复杂的object,如list中套着list的情况,shallowcopy中的子list,并未从原object真的「独立」出来。也就是说,如果你改变原object的子list中的一个元素,你的copy就会跟着一起变。这跟我们直觉上对「复制」的理解不同。
看不懂文字没关系我们来看代码:
>>>importcopy >>>origin=[1,2,[3,4]] #origin里边有三个元素:1,2,[3,4] >>>cop1=copy.copy(origin) >>>cop2=copy.deepcopy(origin) >>>cop1==cop2 True >>>cop1iscop2 False #cop1和cop2看上去相同,但已不再是同一个object >>>origin[2][0]="hey!" >>>origin [1,2,['hey!',4]] >>>cop1 [1,2,['hey!',4]] >>>cop2 [1,2,[3,4]] #把origin内的子list[3,4]改掉了一个元素,观察cop1和cop2
可以看到cop1,也就是shallowcopy跟着origin改变了。而cop2,也就是deepcopy并没有变。
似乎deepcopy更加符合我们对「复制」的直觉定义:一旦复制出来了,就应该是独立的了。如果我们想要的是一个字面意义的「copy」,那就直接用deep_copy即可。
那么为什么会有shallowcopy这样的「假」copy存在呢?这就是有意思的地方了。
python的数据存储方式
Python存储变量的方法跟其他OOP语言不同。它与其说是把值赋给变量,不如说是给变量建立了一个到具体值的reference。
当在Python中a=something应该理解为给something贴上了一个标签a。当再赋值给a的时候,就好象把a这个标签从原来的something上拿下来,贴到其他对象上,建立新的reference。这就解释了一些Python中可能遇到的诡异情况:
>>a=[1,2,3] >>>b=a >>>a=[4,5,6]//赋新的值给a >>>a [4,5,6] >>>b [1,2,3] #a的值改变后,b并没有随着a变 >>>a=[1,2,3] >>>b=a >>>a[0],a[1],a[2]=4,5,6//改变原来list中的元素 >>>a [4,5,6] >>>b [4,5,6] #a的值改变后,b随着a变了
上面两段代码中,a的值都发生了变化。区别在于,第一段代码中是直接赋给了a新的值(从[1,2,3]变为[4,5,6]);而第二段则是把list中每个元素分别改变。
而对b的影响则是不同的,一个没有让b的值发生改变,另一个变了。怎么用上边的道理来解释这个诡异的不同呢?
首次把[1,2,3]看成一个物品。a=[1,2,3]就相当于给这个物品上贴上a这个标签。而b=a就是给这个物品又贴上了一个b的标签。
第一种情况:
a=[4,5,6]就相当于把a标签从[1,2,3]上撕下来,贴到了[4,5,6]上。
在这个过程中,[1,2,3]这个物品并没有消失。b自始至终都好好的贴在[1,2,3]上,既然这个reference也没有改变过。b的值自然不变。
第二种情况:
a[0],a[1],a[2]=4,5,6则是直接改变了[1,2,3]这个物品本身。把它内部的每一部分都重新改装了一下。内部改装完毕后,[1,2,3]本身变成了[4,5,6]。
而在此过程当中,a和b都没有动,他们还贴在那个物品上。因此自然ab的值都变成了[4,5,6]。
搞明白这个之后就要问了,对于一个复杂对象的浅copy,在copy的时候到底发生了什么?
再看一段代码:
>>>importcopy >>>origin=[1,2,[3,4]] #origin里边有三个元素:1,2,[3,4] >>>cop1=copy.copy(origin) >>>cop2=copy.deepcopy(origin) >>>cop1==cop2 True >>>cop1iscop2 False #cop1和cop2看上去相同,但已不再是同一个object >>>origin[2][0]="hey!" >>>origin [1,2,['hey!',4]] >>>cop1 [1,2,['hey!',4]] >>>cop2 [1,2,[3,4]] #把origin内的子list[3,4]改掉了一个元素,观察cop1和cop2
学过docker的人应该对镜像这个概念不陌生,我们可以把镜像的概念套用在copy上面。
copy对于一个复杂对象的子对象并不会完全复制,什么是复杂对象的子对象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是复杂对象的子对象。对于子对象,python会把它当作一个公共镜像存储起来,所有对他的复制都被当成一个引用,所以说当其中一个引用将镜像改变了之后另一个引用使用镜像的时候镜像已经被改变了。
所以说看这里的origin[2],也就是[3,4]这个list。根据shallowcopy的定义,在cop1[2]指向的是同一个list[3,4]。那么,如果这里我们改变了这个list,就会导致origin和cop1同时改变。这就是为什么上边origin[2][0]=“hey!”之后,cop1也随之变成了[1,2,[‘hey!',4]]。
deepcopy的时候会将复杂对象的每一层复制一个单独的个体出来。
这时候的origin[2]和cop2[2]虽然值都等于[3,4],但已经不是同一个list了。即我们寻常意义上的复制。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。