Python3 & 浅拷贝与深拷贝

Python018

Python3 & 浅拷贝与深拷贝,第1张

在Python中对象的赋值(=)其实就是对象的引用。即:当创建一个对象,把它赋值给另一个变量时,python并没有拷贝这个对象,只是拷贝了这个对象的引用而已。

Python中对象的拷贝分为:浅拷贝(copy)和深拷贝(deepcopy)。

浅拷贝:拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。也就是,将原对象在内存中引用地址拷贝过来,然后让新的对象指向这个地址。可以使用“=”或列表自带的copy()函数(如list.copy()),或使用copy模块的copy()函数。

深拷贝:外围和内部元素都进行了拷贝对象本身,而不是引用。即把对象复制一遍,并且该对象中引用的其他对象也同时复制,完全得到一个新的一模一样的对象,对新对象里的值进行修改不会影响原有对象,新对象和原对象完全分离开。深拷贝只能使用copy模块中deepcopy()函数,使用前要导入:from copy import deepcopy。

Python中对象分为不可变对象 、可变对象。

不可变对象:一旦创建就不可修改的对象,例如:字符串、元组、数字

可变对象:可以修改的对象,例如:列表、字典。

其中Python中的切片可以应用于:列表、元组、字符串,但不能应用于字典。

而深浅拷贝,可应用于序列(列表、元组、字符串),也可应用于字典。

其中不可变对象,不管是深拷贝还是浅拷贝,地址值在拷贝后的值都是一样的。

以下以元组(不可变类型)为例

从上述示例可以看出:

不可变对象类型,没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。

所以不可变类型,不管是深拷贝还是浅拷贝,地址值和拷贝后的值都是一样的。

以下以列表(可变类型)为例

第一种方法:使用=号浅拷贝

输出结果:

第二种方法:使用copy浅拷贝

输出结果:

第三种方法:使用deepcopy深拷贝

输出结果:

从上述示例可以看出:

=浅拷贝:值相等,地址相等

copy浅拷贝:值相等,地址不相等

deepcopy深拷贝:值相等,地址不相等

总结:

1,深浅拷贝都是对源对象的复制,占用不同的内存空间。

2,不可变类型的对象,对于深浅拷贝毫无影响,最终的地址值和值都是相等的。

3,可变类型的对象,使用=浅拷贝时, 值相等,地址相等,对新对象里的值进行修改同时会影响原有对象使用copy浅拷贝时值相等,地址不相等使用deepcopy深拷贝时值相等,地址不相等。可以看出针对可变类型copy浅拷贝和deepcopy深拷贝,对新对象里的值进行修改不会影响原有对象。

浅拷贝是对一个对象父级(外层)的拷贝,并不会拷贝子级(内部),而深拷贝对一个对象是所有层次的拷贝(递归),内部和外部都会被拷贝过来。

使用浅拷贝的时候,分为两种情况。

第一种,如果最外层的数据类型是可变的,比如说列表,字典等,浅拷贝会开启新的地址空间去存放。

第二种,如果最外层的数据类型是不可变的,比如元组,字符串等,浅拷贝对象的时候,还是引用对象的地址空间。

深拷贝也分两种情况:

第一种,最外层数据类型可变。这个时候,内部和外部的都会拷贝过来。

第二种,外层数据类型不可变,如果里面是可变数据类型,会新开辟地址空间存放。如果内部数据类型不可变,才会如同浅拷贝一样,是对地址的引用。

对于Python的初学者,在对象的使用过程中,由于对变量的赋值和对象的复制中的概念模糊,导致程序出错。

例如,下面的代码:

输出结果为:

a = [6,2,3,4,5],

b = [6,2,3,4,5],

c = [1,2,3,4,5]

a等于b?True

a等于c?True

a是b?True

a是c? False

可以看到,a,b, c所指向的对象的值都相同(a==b为True). a和b都是代表同一个对象(a is b为True)。当我们通过变量b对该列表进行修改时,由于a也指向该列表,所以当打印a,b时,我们得到相同的值。 而a和c则是代表不同的对象(a is c为False),所以修改b所指向得列表不会改变c梭子乡的列表的值.

在Python中,所有的变量都代表了对象,即便是简单的数字类型(int, float, bool),也是以对象的形式存在的。我们看下面的代码:

输出结果是:

a==b为True

a is b为True

可见,a, b都是指向同一个对象。接下来,进行下面的操作,

输出结果是:

a = 1, b = 2

a is b为False

与前面的列表不同,当我们对b做修改时,实际上是给b赋予了一个新生成的对象,对数值类型来说,所有的数值运算都会创建一个数值对象,并将这个对象指定给变量。因此,a与b指向了不同的对象,数值也不同。

再回过头来看列表对象,

我们知道,b是与a指向同一对象的变量,使用b对该对象进行修改,与使用a对该对象进行修改,效果是完全一样的。如果我们需要需要一个与a完全相同又与a相互独立的列表,那么就需要复制这个对象,也就是新建一个内容和源对象相同的对象。

对于列表来说,最简单的复制方法是通过下标索引的方式创建新的列表:

对于各种数据类型通用的对象拷贝复制,我们可以使用python内建的copy模块。

对于复杂对象(如嵌套列表)的复制,则需要注意区分浅拷贝和深拷贝。我们来看下面的代码:

得到的结果是:

a[0] is b[0]为 True

a[0] is c[0]为 False

a = [[-1, 2, 3], [4, 5, 6]]

b = [[-1, 2, 3], [7, 8, 9]]

c = [[1, 2, 3], [4, 5, 6]]

a[1] is b[1]为False

从上面的代码我们可以看到,copy函数为浅拷贝,只拷贝了对象的外层,而对象内部所包含的对象仍然指向原有的对象。而deepcopy则为深拷贝,对象内部的对象也进行了复制。

以上我们对变量的赋值和对象的复制做了更加深入的分析。在具体的使用中,我们需要根据具体来决定使用赋值、浅拷贝、深拷贝。