首页 > 编程语言 >Python中的深拷贝与浅拷贝

Python中的深拷贝与浅拷贝

时间:2024-10-21 11:20:33浏览次数:1  
标签:Python 整数 列表 对象 拷贝 copy id

目录

1. 可变对象和不可变对象

在 Python 中,数据类型可以分为两大类:可变对象不可变对象

常见的可变对象有:

  • 列表(list)
  • 字典(dict)
  • 集合(set)

常见的不可变对象有:

  • 整数(int)

  • 浮点数(float)

  • 字符串(str)

  • 元组(tuple)

  • 布尔值(bool)

可变对象的值可以在对象创建后被修改,即可以在不改变内存地址的情况下修改对象的内容。

例如:

>>> a = [0,1]
>>> id_old = id(a)	# 修改前a的地址
>>> a.append(1)
>>> id_new = id(a)	# 修改后a的地址
>>> print(id_old == id_new)
True

a = [0,1]:创建了一个列表对象,id_old是这个列表对象的地址。

a.append(2):我们想要在a的末尾添加2。由于列表是可变对象,因此可以直接修改。id_new为修改后a的地址,比较后发现,id_new=id_old,说明修改前后的内存地址没有发生改变。也就是说,Python直接对a进行了修改。

不可变对象不允许修改对象的内容。如果需要更改内容,必须创建一个新的对象。

例如:

>>> b = 1
>>> id_old = id(b)	# 修改前b的地址
>>> b = 2
>>> id_new = id(b)	# 修改后b的地址
>>> print(id_old == id_new)
False

b=1:创建了一个整数对象,id_old是这个整数对象的地址。

b=2:我们想要把b修改为2。但是由于整数是不可变对象,因此必须再创建一个新的整数对象,并令其名称为b,值为2,is_new是这个整数对象的地址。由于id_old和id_new是两个整数对象的地址,所以id_old不等于id_new。

在接下来的内容中,我将以整数对象为例,讲解不可变对象的性质;以列表对象为例,讲解可变对象的性质

让我们画一个图,以便于更加清晰表示上面的例子:

image-20241021111109572

对于列表:Python可以直接对列表进行修改,因此,修改前后,内存中只有一个列表。

对于整数:Python不能修改整数的值。如果要修改整数的值,必须创建一个新的整数对象,因此,在修改前后,内存中有两个整数。一个是修改前的整数1,一个是新创建的整数2。

2. 用=赋值的问题

当我们使用=来直接赋值时,Python 不会创建一个新的对象,而只会创建一个新的引用。

例如,

>>> c = [1,2]
>>> id_c = id(c)
>>> d = c
>>> id_d = id(d)
>>> print(id_c == id_d)
True

当我们执行d=c时,Python不会为变量d创建一个新的列表,而是让变量c和d共享同一个列表,c和d只不过是对同一个列表的两个引用。

如下图

image-20241020214922555

因此,当我们改变变量c中的值时,会发现,变量d中的值也被改变了。

>>> c.append(3)
>>> print(d)
[1,2,3]

原因是因为,c和d共同指向的列表的值发生了变化,如下图

image-20241020215054889

因此,d的值也改变了。

需要注意的是,对于不可变对象,如果有如下的代码

>>> x = 1
>>> y = x
>>> x = 2
>>> print(y)
1

你会发现,y的值并没有随着x的改变而改变。这是因为,整数对象是不可变对象。

在执行y=x后,x、y和整数1的关系如下图,此时,x,y是同一个整数的两个引用

执行x=2后,由于整数是不可变对象,因此Python会创建一个新的值为2的整数,并将这个整数赋给x,如下图

image-20241020215715045

可以看到,y还是指向原来的整数,并没有受到影响,因此,y的值不会改变

3. copy模块登场

为了解决上面的问题,copy模块闪亮登场。

copy模块中,只有两个函数,copydeepcopy

看下面的例子

>>> x = [1,2]
>>> y = copy.copy(x)
>>> z = copy.deepcopy(x)
>>> x.append(3)
>>> print(y)
>>> print(z)
[1,2]
[1,2]

可以看到,在使用了copydeepcopy之后,在对x进行修改时,y和z的值就不会跟着发生变化了。

那么,copydeepcopy有什么区别呢?

为此,我们需要重新认识一下列表对象,以及了解一下浅拷贝和深拷贝的概念

4. 重新认识列表对象

考虑下面这两个列表,其中x是一维的列表,y是二维的列表

>>> x = [1,2,3]
>>> y = [[1,2,3],[4,5,6],[7,8,9]]

其中x所对应的一维列表,在内存中是这样的:

image-20241020222915705

x是列表对象的引用。在列表对象中,存着三个地址:分别是整数1的地址,整数2的地址以及整数3的地址。


至于y所对应的二维列表,在内存中是这样的:

image-20241021102039344

y是二维列表的引用。在二维列表中,存着三个一维列表的地址。在一维列表中,分别存着其中整数元素的地址。

5. 浅拷贝,深拷贝

知道列表对象在内存中是什么样子的,就可以引出浅拷贝和深拷贝了。

浅拷贝(copy.copy())

假设我们令

>>> y = [[1,2,3],[4,5,6],[7,8,9]]
>>> z = copy.copy(y)

那么,在内存中,执行的操作如下:

image-20241021103146372

Python复制了一个二维的列表,并将这个二维列表赋给了z。

但需要注意的是,旧的二维列表中的地址,和新的二维列表中的地址是相同的。也就是说,新旧两个二维列表指向相同的一维列表。

为了方便,这里省略了整数对象

这就是浅拷贝的含义,浅拷贝只复制“最上层”的列表。


如果我们执行下面的代码

>>> y = [[1,2,3],[4,5,6],[7,8,9]]
>>> z = copy.copy(y)
>>> z.append([1,2,3])
>>> print(y)
>>> z[0] = [1,2]
>>> print(y)
[[1,2,3],[4,5,6],[7,8,9]]
[[1,2],[4,5,6],[7,8,9]]

可以看到,当向z中添加元素时,y的值不会变化。而修改z的第一个列表时,y的值也会发生变化。


当向z中添加元素时, 内存中的操作如下

image-20241021103231925

可以看到,此时,在z对应的二维列表中,新添加了一个地址4。地址4指向的就是z中新添加的元素。

不过,虽然z中多了一个地址4,但是y中的地址没有发生变化,因此,不会影响到y对应的二维列表。


当修改z的第一个列表时,内存中的操作如下

红色的列表表示被修改的列表。

可以看到,y和z中,都有指向红色列表的地址。因此一旦红色列表被修改,那么y和z的值都会改变。

一维列表的浅拷贝

考虑下面的代码

>>> x = [1,2,3]
>>> z = copy.copy(x)

内存中的操作如下

image-20241021104844717

同样的,Python复制了一个新的一维列表,并将这个新一维列表赋给了z。

同时,旧一维列表中的地址,和新一维列表中的地址是相同的。也就是说,新旧两个一维列表指向相同的整数。

当我们执行下面的代码时:

>>> z[0] = 0

由于整数是不可变对象,而不可变对象不允许修改对象的内容,如果需要更改内容,必须创建一个新的对象。

因此我们执行z[0]=0时,实际上创建了一个新的整数对象,并令新的整数对象的值为0

image-20241021104935399

这时候,x所指的整数没有发生变化,因此x的值不会变。

深拷贝(copy.deepcopy())

假设我们令

>>> y = [[1,2,3],[4,5,6],[7,8,9]]
>>> z = copy.deepcopy(y)

那么此时,内存中执行的操作如下

此时,Python不再只是复制最上层的列表,而是将y中所有的元素都完完整整的复制了一遍,并赋给了z。因此,这时候,无论对z进行任何操作,都无法影响y的值了。

浅拷贝,深拷贝,直接赋值的区别

>>> y = [[1,2,3],[4,5,6],[7,8,9]]
>>> z1 = y	# 直接赋值
>>> z2 = copy.copy(y)	# 浅拷贝
>>> z3 = copy.deepcopy(y)	# 深拷贝
>>>
>>> z1.append([1,2,3]) # 会改变y的值
>>> z1[0] = [1,2]	# 会改变y的值
>>>
>>> z2.append([1,2,3])	# 不会改变y的值
>>> z2[0] = [1,2]	# 会改变y的值
>>>
>>> z3.append([1,2,3])	# 不会改变y的值
>>> z3[0] = [1,2]	# 不会改变y的值

总结一下吧

image-20241021105954767

标签:Python,整数,列表,对象,拷贝,copy,id
From: https://www.cnblogs.com/rh-li/p/18489103

相关文章