今天复习了一下浅拷贝和深拷贝,想写篇文章记录一下,小白话一篇文章讲清楚会有点冗余,因此先把作为基础的可变与不可变类型给讲述一遍,作为铺垫,日后再对浅拷贝和深拷贝进行详解。
可变与不可变类型讲浅拷贝和深拷贝之前,首先要讲的是可变与不可变类型,那么什么是可变与不可变类型呢,那就要先讲讲变量在内存中是如何存储的。
内存中有堆区和栈区,如下图所示,变量与值的关系其实是一种类似于绑定的关系,其中变量在栈中,值在堆中,栈中变量指向堆中的值。例如如下代码,在内存中其实是如下一种关系:
x=10
假如此时,重新对x进行赋值,即x=20,这样x就会先和10解除绑定关系,然后与20进行绑定,先前的10不会受到任何影响,也就是说只是更换了一种绑定关系,值是没有变的,这就是所谓的“不可变”类型了。其中int、float、str、tuple都是不可变类型。
这里再多说一点,python中正常的赋值可以理解为内存地址的赋值,例如这里我加一句y=x,这样x就把20的内存地址赋给了y,y也和20绑定了,如下图所示。
绑定之后无论x怎么变化,y就一直与20绑定,可以通过如id()函数进行确认,如下代码。
x=20 y=x print(id(x), id(y)) # 两者输出一致,都是20的地址` x=10 print(id(x), id(y)) #y的地址与之前一致, x的地址发生了改变可变类型
现在来讲讲什么是可变类型,首先我们看list是如何在内存中存放的。依旧是一种绑定关系,变量名在栈区,变量在堆区,如下代码
x=[10, 20]; y=x;在内存中对应下图:
其中x与堆中的list进行绑定,进行赋值操作中,便将y与此list进行绑定。注意如果这里直接对x进行赋值,与刚刚讲述的不可变类型同理其实就是更换了绑定关系,并不影响list与y之前的绑定。但如果通过语句x[1]=30,那么在内存中会发生如下变化,也就是list的第一个元素将会从20指向30,也可以理解成一种解绑与再绑定的过程,注意这个过程中并不影响x, y和list之间的绑定关系。这时打印y, 可以发现也变成了[10, 30]。这就是可变类型,一旦绑定之后值仍然可能会发生变化就是可变类型,从内存的角度去分析其实就是,可变类型可以影响到堆中的绑定关系,不可变类型只能改变栈和堆的绑定关系。此外,字典和集合也为可变类型。
这里同样可以通过代码去验证:
x = [10, 20] y = x x[1] = 30 print(x, y) ##这里打印的结果都为[10, 30]总结
综上所述,不可变类型只会影响第一层(堆和栈之间的)绑定关系,可变类型既可以影响第一层,也可以第二层(堆中)的绑定关系



