Python深拷贝(deepcopy)、浅拷贝(copy)、等号拷贝的深入理解----看了还不懂找我
编程中难免会遇到copy(浅拷贝)与deepcopy(深拷贝)问题,那么你知道等号(=)拷贝和浅拷贝(copy)有什么区别吗?也许很少有人对二者的区别能讲出一二三吧,看完这篇文章,你就会知道python的变量存储机制,以及深浅拷贝以及等号拷贝的差异所在了。
对于Python中的拷贝问题我也查阅了不少资料,在网上也没有找到如意的讲解,进过一段时间的深入理解之后,我把理解的内容写在这里,供大家参考,不对的地方欢迎大家批评指正。
首先在python中,什么是浅拷贝,什么是深拷贝呢?什么是等号拷贝(形如:bb = aa)呢?有谁能一句话解释清楚,他们的区别又在哪里?-----一定记住针只有对复杂对象才有区别意义!!!
主要是针对复杂结构对象,复杂结构对象就是嵌套两层及以上的子对象,比如:即列表中嵌套子列表,像[1, [2, 3]]这种结构
复杂对象中的深浅拷贝:
一句话解释(=)等号拷贝:当于对于电脑中某个文件夹新建了一个快捷图标,快捷图标永远和原文件是一致的。
一句话解释(copy)浅拷贝:相当于对于电脑中某个文件夹内部的所有子文件及子文件夹新建了快捷图标,放到新的文件夹中。
一句话解释(deepcopy)深拷贝:相当于对于电脑中某个文件夹用u盘拷贝了一个备份。所以原来电脑中文件夹内文件改变时,u盘的文件是不会变化的。
要弄清楚拷贝原理,首先应该弄清楚Python变量存储机制
Python中变量的存储机制
1. aa = 1的存储机制
当aa = 1 时,首先Python会在内存中新开辟一个空间存储数字“1”,然后将该内容的地址赋值给变量‘aa’。有点像如下图所示
2. bb = aa的复制机制
对于正常的“=” 赋值, 比如 bb = aa,则有
3. or_list = [1, [2, 3]]存储机制
Python中复杂对象,等号拷贝,copy浅拷贝,deepcopy深拷贝机制
1. 拷贝后的差异
看以下代码初始列表为 or_list = [1, [2, 3]],分别进行"="拷贝,copy浅拷贝,deepcopy深拷贝
操作如下,各个拷贝后列表内存位置通过id()可以看到差异
1. 初始列表 or_list = [1, [2, 3]]
2. 进行以下三个复制操作
eq_list = or_list
sh_list = copy(or_list)
de_list = deepcopy(or_list)
3. 各个list的值为:
or_list = [1, [2, 3]]
eq_list = [1, [2, 3]]
sh_list = [1, [2, 3]]
de_list = [1, [2, 3]]
4. =====各变量ID的值===========
id(or_list)= 2269079198528
id(eq_list)= 2269079198528
id(sh_list)= 2269076677056
id(de_list)= 2269076677632
5. ======列表第一个位置的ID值=======
id(or_list[0]) = 2269075013872
id(eq_list[0]) = 2269075013872
id(sh_list[0]) = 2269075013872
id(de_list[0]) = 2269075013872
6. ======列表第二个位置的ID值===========
id(or_list[1]) = 2269076677312
id(eq_list[1]) = 2269076677312
id(sh_list[1]) = 2269076677312
id(de_list[1]) = 2269077081216
6.1 ====列表第二个位置的子列表第一位置的ID值=====
id(or_list[1][0]) = 2269075013904
id(eq_list[1][0]) = 2269075013904
id(sh_list[1][0]) = 2269075013904
id(de_list[1][0]) = 2269075013904
6.2 ====列表第二个位置的子列表第二位置的ID值=====
id(or_list[1][1]) = 2269075013936
id(eq_list[1][1]) = 2269075013936
id(sh_list[1][1]) = 2269075013936
id(de_list[1][1]) = 2269075013936
如果纯文字不好理解,可以参考以下示意图
2. 子对象内的内容被改变
进行or_list[1][0] = 4操作,结果如下所示:
可以看出原始该位置存储的id是*176,初始列表该位置发生改变时,等号拷贝和copy浅拷贝也跟着发生了改变,但是deepcopy深拷贝没有发生变化。为什么会这样呢?改变的原理可以看下面的示意图。因为deepcopy是在List[1]位置就创建了新的内存空间,其它拷贝并没有,他们还是引用的原始列表相同的内存空间,所以才会跟着一起变化。
======更改前:or列表的子列表第二个位置的id值===========
id(or_list[1][0])= 2269075013904
id(eq_list[1][0])= 2269075013904
id(sh_list[1][0])= 2269075013904
id(de_list[1][0])= 2269075013904
进行更改: or_list[1][0] = 4
======更改后:or列表的子列表第二个位置的ID值===========
id(or_list[1][0])= 2269075013968
id(eq_list[1][0])= 2269075013968
id(sh_list[1][0])= 2269075013968
id(de_list[1][0])= 2269075013904
======更改后变量值===========
or_list_after_change = [1, [4, 3]]
eq_list_after_change = [1, [4, 3]]
sh_list_after_change = [1, [4, 3]]
de_list_after_change = [1, [2, 3]]
3. 改变List[0]位置的值时
改变第一层的元素时,只有等号拷贝跟着改变了,是因为copy浅拷贝和deepcopy深拷贝都是新开辟了内存空间,存储List[0],List[1],所以当原始的List[0]发生改变时,这两个拷贝没有任何影响,具体原理查看以下示意图
======更改前:or列表的第一个位置的ID值===========
id(or_list[0])= 2269075013872
id(eq_list[0])= 2269075013872
id(sh_list[0])= 2269075013872
id(de_list[0])= 2269075013872
进行更改: or_list[0] = 5
======更改后:or列表的第一个位置的ID值===========
id(or_list[0])= 2269075014000
id(eq_list[0])= 2269075014000
id(sh_list[0])= 2269075013872
id(de_list[0])= 2269075013872
======更改后变量值===========
or_list_after_change = [5, [4, 3]]
eq_list_after_change = [5, [4, 3]]
sh_list_after_change = [1, [4, 3]]
de_list_after_change = [1, [2, 3]]
4. 改变List[1]位置的值时
原理同3改变List[0]位置的值。原理和代码参考以下代码是示意图
======更改前: or列表的第一个位置的ID值===========
id(or_list[1])= 2269076677312
id(eq_list[1])= 2269076677312
id(sh_list[1])= 2269076677312
id(de_list[1])= 2269077081216
进行更改: or_list[1] = {'1': 'a', 'b': 1}
======更改后: or列表的第一个位置的ID值===========
id(or_list[1])= 2269076306688
id(eq_list[1])= 2269076306688
id(sh_list[1])= 2269076677312
id(de_list[1])= 2269077081216
======更改后变量值===========
or_list_after_change = [5, {'1': 'a', 'b': 10}]
eq_list_after_change = [5, {'1': 'a', 'b': 10}]
sh_list_after_change = [1, [4, 3]]
de_list_after_change = [1, [2, 3]]