这一讲的深浅拷贝对可变类型和不可变类型是有区别的,关于可变类型和不可变类型可以看我的这篇讲解Python提升系列——(1)不可变类型与可变类型
在很多语言中都涉及到深浅拷贝问题,因为在不同的场景中深浅拷贝都有对应的应用,如果用混淆了,则会出现bug。
在讲解深浅拷贝之前,先看一下Python中的赋值问题。
赋值
都知道赋值是通过**“=”**进行的,那将一个变量赋值给另一个变量后,当其中一个变量修改了,另外一个变量会不会变呢?
这个得根据数据是可变类型还是不可变类型分开讲
不可变类型
我在Python提升系列1中讲过不可变类型是指内存和值都不变,包括:数值、字符串、元组和布尔类型。
所以,当一个变量是不可变类型的引用的,当它被赋值给另一个变量后,其中一个的修改,不会影响另外一个变量,因为会重新开辟一块新的内存,变量会指向新的引用。
如:
- 变量1 = 不可变类型1
- 变量2 = 变量1
- 变量1 = 不可变类型2
结果:
变量1 -----> 不可变类型2(地址1)
变量2 -----> 不可变类型1(地址2)
下面看一下代码:
# 数值
num1 = 1
num2 = num1
num2 = 2
print(num1) # 1
print(num2) # 2
print(id(num1)) # 140732872692544
print(id(num2)) # 140732872692576
# 字符串
str1 = "hello"
str2 = str1
str2 = "hello1"
print(str1) # hello
print(str2) # hello1
print(id(str1)) # 1995804017808
print(id(str2)) # 1995810605800
可变类型
相对于不可变类型的赋值,可变类型的赋值会使两个变量指向同一个地址,其中一个变量的修改会影响另一个变量。
可变类型有:列表、字典、集合
下面看一下代码:
# 列表
a = [11, 22]
b = a
a.append(33)
print(a) # [11, 22, 33]
print(b) # [11, 22, 33]
print(id(a)) # 2343423468104
print(id(b)) # 2343423468104
# 字典
c = {"name": "xia"}
d = c
c["age"] = 18
print(c) # {'name': 'xia', 'age': 18}
print(d) # {'name': 'xia', 'age': 18}
print(id(c)) # 1835636063040
print(id(d)) # 1835636063040
# 集合
set1 = {"hello", "world"}
set2 = set1
set1.add("python")
print(set1) # {'python', 'world', 'hello'}
print(set2) # {'python', 'world', 'hello'}
print(id(set1)) # 2145586527816
print(id(set2)) # 2145586527816
深浅拷贝
由于可变类型的赋值,会导致两个变量指向同一块地址,变量的修改会相互影响,于是有了深浅拷贝。
在Python中实现深浅拷贝主要是copy模块。下面讲一下copy模块的使用:
- copy.copy()被称为浅拷贝
- copy.deepcopy()被称为深拷贝
不可变类型
看一下分别使用copy.copy()和copy.deepcopy()有什么作用。
copy.copy()
import copy
# 数值
num1 = 1
num2 = copy.copy(num1)
num2 = 2
print(num1) # 1
print(num2) # 2
print(id(num1)) # 140732872692544
print(id(num2)) # 140732872692576
# 字符串
str1 = "hello"
str2 = copy.copy(str1)
str2 = "hello1"
print(str1) # hello
print(str2) # hello1
print(id(str1)) # 1995804017808
print(id(str2)) # 1995810605800
结论:对于不可变类型,可以看到,两个变量还是指向不同的地址,互不影响,与未使用copy.copy()直接赋值的效果一样。
copy.deepcopy()
import copy
# 数值
num1 = 1
num2 = copy.deepcopy(num1)
num2 = 2
print(num1) # 1
print(num2) # 2
print(id(num1)) # 140732872692544
print(id(num2)) # 140732872692576
# 字符串
str1 = "hello"
str2 = copy.deepcopy(str1)
str2 = "hello1"
print(str1) # hello
print(str2) # hello1
print(id(str1)) # 1995804017808
print(id(str2)) # 1995810605800
结论:对于不可变类型,使用copy.deepcopy()与使用copy.copy()、赋值的效果相同。
可变类型
copy.copy()
# 列表
import copy
list1 = [1, 2, 3]
list2 = copy.copy(list1)
list1.append(4)
print(list1) # [1, 2, 3, 4]
print(list2) # [1, 2, 3]
print(id(list1)) # 2056712713736
print(id(list2)) # 2056712711560
print("-------------")
a = [11, 22, [1, 2]]
b = copy.copy(a)
a[-1][-1] = 100
a.append(33)
print(a) # [11, 22, [1, 100], 33]
print(b) # [11, 22, [1, 100]]
print(id(a)) # 2343423468104
print(id(b)) # 2343423468104
copy.deepcopy()
# 列表
import copy
list1 = [1, 2, 3]
list2 = copy.deepcopy(list1)
list1.append(4)
print(list1) # [1, 2, 3, 4]
print(list2) # [1, 2, 3]
print(id(list1)) # 2510491530568
print(id(list2)) # 2510491460296
print("-------------")
a = [11, 22, [1, 2]]
b = copy.deepcopy(a)
a[-1][-1] = 100
a.append(33)
print(a) # [11, 22, [1, 100], 33]
print(b) # [11, 22, [1, 2]]
print(id(a)) # 2510491640584
print(id(b)) # 2510491640328
深浅拷贝与js中的很像,可以参考下图:
通过大量的例子,我们得出结论:
在不可变数据类型中,深浅拷贝都不会开辟新的内存空间,用的都是同一个内存地址。
在存在嵌套可变类型的数据时,深浅拷贝都会开辟新的一块内存空间;同时,不可变类型的值还是指向原来的值的地址。
不同的是:在嵌套可变类型中,浅拷贝只会拷贝最外层的数据,而深拷贝会拷贝所有层级的可变类型数据。
参考资料:
https://mp.weixin.qq.com/s/e8N-s2w4gYQPKETVH62EAg