Python 深拷贝浅拷贝

Python 深拷贝浅拷贝

大家肯定在学习Python基础知识的时候,已经学过这个了。但是我为什么写在这里呢?因为这里涉及到的知识不仅是深拷贝和浅拷贝还有我们的可变数据结构以及不可变数据结构,说到这个可能大家就会有点陌生了,这又是什么呢?有什么联系呢?那么一起来学习吧!

深拷贝浅拷贝

浅拷贝

在Python 中我们经常会听见深拷贝和浅拷贝的概念,那么什么是浅拷贝呢? 下面代码来演示!

1
2
3
4
a = [1,2,3]
b = a
print(a==b) # True
print(a is b) # True

大家可以从上面的代码中看到,声明了一个列表变量名字为a, 然后将 a 赋值给了 b, 这时候我们查看 a==b a is b 可以看出打印出来的都是true, 那么下面我会进行其他的操作:

1
2
3
4
5
6
7
8
a = [1,2,3]
b = a
print(a==b)
print(a is b)

a.append(4)
print(a==b) # True
print(a is b) # True

我在上面的操作中是在a的列表中添加了一个4,但是此时我在执行 a==b, a is b 结果仍旧是True,那么大家思考为什么呢?明明我将[1,2,3]也就是初始的a 赋值给了 b, 但是此时我并再次赋值给b,为何会还是一样的呢?那么就是我们要说的浅拷贝了。

浅拷贝:也就是在内存中重新申请一块,然后存储你浅拷贝的那个对象的地址引用。

也就是期初在你赋值的时候,b里面存放的是a的内存地址的引用,访问b的时候实际访问的还是a的内存地址,a 和 b 都指向同一个内存对象,当我们修改了a的列表内容的时候,访问b的时候也就会访问到我们最新修改的a。

1
2
3
4
5
6
7
8
9
10
11
12
13
a = [1,2,3]
b = a
print(a==b)
print(a is b)
print(id(a)) # 138984912667136
print(id(b)) # 138984912667136

a.append(4)
print(a==b)
print(a is b)

print(id(a)) # 138984912667136
print(id(b)) # 138984912667136

那么经过上述的查看a,b的内存地址就可以更加了解了,那么下面总结一下:

深拷贝

代码演示:

1
2
3
4
5
6
import copy

a = [1,2,3]
b = copy.deepcopy(a)
print(a==b) # True
print(a is b) # False

此时我们引入了python自带的copy函数,使用了其中的deepcopy方法,这个方法就是深拷贝,当我们使用这个方法的时候,就和上面的浅拷贝不一样了,我们可以看到a和b 的值依旧是一样的,但是a is b 是False, 这是为什么呢? 下面我们加两行代码就知道了!

1
2
3
4
5
6
7
8
import copy

a = [1,2,3]
b = copy.deepcopy(a)
print(a==b)
print(a is b)
print(id(a)) # 134937665356864
print(id(b)) # 134937667268096

那么到这里我相信很多同学已经明白了深拷贝与浅拷贝的不同之处了!

深拷贝:声明的内存中真正的存储被拷贝的值,不在引用源数据的内存地址。

那么我们在使用深拷贝的时候就是真正的复制了值到了b中,a和b 也就完全独立了互不影响!可以通过下面的代码来看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import copy

a = [1,2,3]
b = copy.deepcopy(a)
print(a==b)
print(a is b)
print(id(a)) # 134937665356864
print(id(b)) # 134937667268096

a.append(4)
print(a==b) # False
print(a is b) # False

print(id(a)) # 134937665356864
print(id(b)) # 134937667268096

那么到此我想你已经明白了深拷贝的含义了!那么刚刚我提到的 可变数据结构 和 不可变数据结构 和这个深拷贝和浅拷贝有什么关系呢?那就让我们一起继续学习吧!

可变数据结构和不可变数据结构

可变数据结构

可变数据结构顾名思义就是这个数据结构可以被改变,比如你可以增加,修改,删除元素。

那么在python 中可变数据结构有以下几种:

  • 列表(list):列表是最常用的可变数据结构之一。你可以向列表中添加、删除或更改元素。

  • 字典(dict):字典存储键值对,其中键必须是唯一的且不可变(通常是字符串或数字),但值可以是任何数据类型,包括可变类型。你可以添加、删除或更改字典中的键值对。

  • 集合(set)可变集合(mutable set):集合是一个无序的不重复元素集。Python中的集合默认就是可变的,你可以向集合中添加或删除元素,但不能直接修改元素(因为集合的元素必须是唯一的,并且通常是不可变的)。

  • 列表推导式、字典推导式等 生成的数据结构类型(根据推导式的内容,可以是列表、字典等),其可变性取决于推导出的数据结构类型。

在刚刚的浅拷贝例子中就可以看到,我们修改了列表中的元素新增了一个,你也可以执行删除,修改等操作。对于字典,集合, 推导式等都可以做到增删更新操作,这里不在介绍基本的python语法,不太了解的可以自己搜索一下!

不可变的数据结构

那么不可变的数据结构就是不允许被更改,比如修改,增加,删除等操作。

那么python中有以下不可变的数据结构:

  • 整数(int)浮点数(float):这些基本数据类型是不可变的,你不能改变一个整数或浮点数的值。

  • 字符串(str):字符串在Python中是不可变的。如果你尝试修改字符串中的某个字符,比如通过索引赋值,Python会抛出一个TypeError。相反,你需要创建一个新的字符串。

  • 元组(tuple):元组也是不可变的。一旦你创建了一个元组,你就不能更改它的元素(尽管如果元组中的元素是可变的,比如列表,你可以修改列表的元素,但元组的结构本身——即元素的顺序和数量——是不可变的)。

以上的数据结构是无法更改内容的,也就是不可变的数据结构,下面代码解释:

1
2
3
4
a = '1'
b = a
print(a==b) # True
print(a is b) # True

那么,我们以字符类型举例,可见我还是我们赋值给了b,两者也是相同的值,相同的内存地址,那么如果我此时修改a的内容会引发什么呢?

1
2
3
4
5
6
7
8
a = '1'
b = a
print(a==b)
print(a is b)

a = '2'
print(a==b) # False
print(a is b) # False

这时可能有同学就会有疑问了,哎,怎么上面刚刚说完浅拷贝如果改了会都发生变化呀,哈哈,这里其实浅拷贝对应如果是可变的数据结构是可以的,但是如果对应到不可变数据结构就不行了,因为我们的数据结构已经定义了不能被改变,所以这就是为什么要将深拷贝浅拷贝和可变数据结构以及不可变结构放在一起介绍的原因。

1
2
3
4
5
6
7
8
9
10
11
12
a = '1'
b = a
print(a==b)
print(a is b)
print(id(a)) # 135575780975872
print(id(b)) # 135575780975872

a = '2'
print(a==b)
print(a is b)
print(id(a)) # 135575780975928
print(id(b)) # 135575780975872

根据上面的例子你会发现,浅拷贝的意义依然是存在的,我们可以看到b的内存与起初a的是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
import copy
a = '1'
b = copy.deepcopy(a)
print(a==b)
print(a is b)
print(id(a)) # 133333075571968
print(id(b)) # 133333075571968

a = '2'
print(a==b)
print(a is b)
print(id(a)) # 133333075572024
print(id(b)) # 133333075571968

从上面的例子也可以发现,即使我们使用了深拷贝,b 也依旧保存的是 a 的内存地址,当a 发生了变化b 也不会发生变化。

那么 我再将上面的可变数据结构的例子放在一起对比一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import copy
a = [1,2,3]
b = copy.deepcopy(a)
print(a==b)
print(a is b)
print(id(a)) # 134937665356864
print(id(b)) # 134937667268096

a.append(4)
print(a==b) # False
print(a is b) # False

print(id(a)) # 134937665356864
print(id(b)) # 134937667268096

那么我们可以发现,在可变数据结构中当我们深拷贝a的时候 此时 a,b的内存地址是不一样的。

总结

深拷贝 和 浅拷贝 在面对可变和不可变的数据结构的时候,对应的表现是不一样的,所以我们要掌握可变数据结构和不可变数据结构,在编写程序的时候选择对应的数据结构才能使得我们的程序更加健壮!


Python 深拷贝浅拷贝
https://dreamshao.github.io/2024/07/22/深拷贝浅拷贝/
作者
Yun Shao
发布于
2024年7月22日
许可协议