Python中的引用和拷贝规律详解

python的可变不可变与各种浅拷贝深拷贝规则,一并梳理。

Python一切皆引用

在C++/Java里,int a = 1就是创建变量为a,赋值为1;int b = a就是创建变量b,赋值为a的值。a与b是毫不相干的,即“变量是盒子”,但是这不利于理解Python中的一个变量定义。在Python里,我们把变量视为“一个实际存储的引用”(图源:《流畅的python》)。

Python中的引用和拷贝规律详解_第1张图片

所以在python里,a = [1, 2, 3]先分配一块区域写入[1,2,3],再让a来代表它;b = a让b与a代表了同一个东西,即使a本身消失了(比如del a),也仅仅是撕下来一张标签而已,b仍然可以访问这个列表。其他类型也是如此

情况一:直接引用

Python中的引用和拷贝规律详解_第2张图片

直接引用即b = a,正如上文所说,不会发生拷贝,只是让b也来代表a代表的区域。此时b就是a,b[0]也就是a[0]。

如果修改了a,等于让a指向其他对象,与列表无关,所以b没有变化;而如果修改a[0](或者使用+=,append等),则修改了列表,b[0]也在变化。

Python中的引用和拷贝规律详解_第3张图片

但对于单个数或者元组字符串这种不可变对象,你也可以使用+=,但是他们不支持原地修改,因此实际上会调用a = a + b得到的是一个新对象。如a = (1, 2, 3); b = a; a += (4, 5),此时执行a = a + (4, 5),已经指向新的值了,所以b不会改变。

情况二:复制

有些时候我们只编辑列表或字典的副本,所以需要复制,一般最常见的复制方法有:

b = a[:]
b = list(_ for _ in a)
b = copy(a)
b = a.copy()

这些都叫做浅复制,浅复制的时候发生了什么?

Python中的引用和拷贝规律详解_第4张图片

浅复制的逻辑将创建一个新对象,然后将每一个值复制一份放入新对象里,花费线性时间。可以看到复制后b与a完全一致,但是a is b不再成立了,a[0]和b[0]也是不再相关的值,你可以任意修改列表b,都不会影响到a里的四个元素(红蓝橙绿四个小圆)。

情况三:深复制

但是浅复制仍然有不能解决的问题。我们知道python里一切皆引用,图里的小圆不是盒子而是标签!,虽然a与b本身已经分开了,但如果有一个元素仍然是列表,那他们其实还是联系在一起的。

Python中的引用和拷贝规律详解_第5张图片

如图,浅复制时执行了b[1]=a[1],但b[1]和a[1]是引用,因此通过他们访问的仍然是同一个可变序列,修改a[1]不会导致b[1]变化,但修改a[1][0]却导致b[1][0]变化。

所以我们引入深复制解决这个问题:

from copy import deepcopy
a = [1, [1, 2, 3], "hello"]
b = deepcopy(a)

深复制的逻辑是,将每一个值复制放进新一个对象里,而如果这一项也表示一个可变的迭代对象(列表,字典,没有特殊定制的自定义类),就将这个对象也复制一份。这样就可以得到一份完全的拷贝。

Python中的引用和拷贝规律详解_第6张图片

总结

到此这篇关于Python中引用和拷贝规律的文章就介绍到这了,更多相关Python引用和拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(Python中的引用和拷贝规律详解)