赋值
Python中的浅拷贝和深拷贝是一个比较重要的知识点,而且面试中经常出现,这里进行一下记录。首先,需要了解“赋值”的概念。 提到赋值,我们会想到赋值操作符“=”,在Python中,我们创建一个对象,然后将对象赋值给变量,实际上是把该对象在地址空间的id地址值拷贝给变量,也就是说“赋值”操作拷贝的是对象的引用。
如下图所示:
创建整数对象5之后,将其赋值给变量a,之后变量a又赋值给变量b,因为赋值操作拷贝的是对象的引用,也就是对象id地址值,因此可以看到a,b和整数对象5具有相同的地址值。后面变量a进行新的赋值操作,变量a对应的是整数对象6的地址值;而变量b仍然对应之前的地址值。
如果,我们创建的对象是列表,不是整数对象,其中列表是可变数据类型,整数是不可变数据类型。对于可变数据类型,每次创建可变数据类型对象的时候,即使内容一样(比如列表中数据元素相同),也会在地址空间分配新的区域。如下图所示:
上图中,因为变量a和b指向同一地址空间,因此当这一地址空间存储的值改变之后,变量a和b的值均改变。
可变数据类型和不可变数据类型
Python中的可变数据类型是指:当该数据类型的变量值发生变化的时候,没有开辟新的内存空间,而是在原来的内存地址上进行修改。可变数据类型包括:列表,字典,集合。
Python中的不可变数据类型是指:当该数据类型的变量值发生变化的时候,原来内存中的值不变,而是会开辟一块新的内存,变量指向该新的内存地址。不可变数据类型包括:整数,浮点数,布尔值,字符串,元组。
浅拷贝
浅拷贝是对一个对象父级(外层)的拷贝,并不会拷贝子级(内部)。在拷贝的时候,需要使用到copy这一Python库。
其中,浅拷贝可以分为两种情况,第一:对象父级(外层)是可变数据类型,比如列表,浅拷贝的时候,会开辟新的内存空间。但是,该对象内部的对象不会进行拷贝。如下图所示:
变量b是可变数据类型,因此浅拷贝的时候,会为变量c开辟新的内存空间,因此变量b和c的地址不同。但是因为是浅拷贝,只会拷贝父级对象(外层),不会拷贝内层,因此内层的可变数据类型地址不会更改。所以变量c中第4个元素的地址就是变量a对应的地址;当变量a的值发生更改的时候,变量b和c均会随之更改;但是,如果变量b的值更改,则不会影响到c。
第二:对象父级(外层)是不可变数据类型,浅拷贝的时候不会开辟新的内存空间,同样对于内层数据无法获取。如下图所示:
变量b是元组,不可变数据类型,因此浅拷贝的时候,变量c和变量b的地址相同。同样,对于内层对象,无法拷贝,因此内层对象a的地址不变,当a的值发生变化的时候,变量b和c随之改变。
深拷贝
深拷贝对一个对象是所有层次的拷贝(递归),内部和外部都会被拷贝过来。
其中,外层对象为可变数据类型和不可变数据类型两种情况下,外层对象的拷贝与浅拷贝相同,不同之处在于内层对象的拷贝。下面,以下图为例:
可以看到,在深拷贝的时候,会对父级对象和内层对象均进行拷贝;因此在深拷贝得到的变量c中,内层对象a的地址也被重新分配,因此更改a的时候,变量c不会受到影响。