Copy and Deepcopy in Python

浅拷贝(Copy)

无论浅拷贝还是深拷贝,在使用前都要先导入库copy

1
import copy

对不可变类型的浅拷贝

Python中的不可变数据类型一般指字符串string、数值number和布尔值bool。想要修改引用了不可变数据类型的变量,我们只能让其引用新的对象:

1
2
3
4
5
6
7
8
9
10
a = 3
print(id(a))
"""
1944168497456
"""
a += 1
print(id(a))
"""
1944168497520
"""

当浅拷贝了引用不可变数据类型的对象时,浅拷贝相当于赋值操作,即两者会引用同一个不可变对象:

1
2
3
4
5
6
a = 5
b = copy.copy(a)
print(id(a), id(b))
"""
1944168497456 1944168497456
"""

对可变类型的浅拷贝

Python中的可变类型指列表list、字典dictionary、集合set以及自定义的类。当变量引用可变类型时,该变量相当于拥有了一个袋子,这个袋子里面装的东西可以随便修改,但是只要变量不引用新的袋子,无论怎么修改原来的袋子,原来的袋子始终是原来的袋子。

1
2
3
4
5
6
7
8
9
10
a = [1, 2, 3]
print(a, id(a))
"""
[1, 2, 3] 1944176728512
"""
a[0] = 2
print(a, id(a))
"""
[2, 2, 3] 1944176728512
"""

当浅拷贝了引用可变数据类型的对象时,Python会直接创造一个新的“袋子”给新对象,但是袋子里的内容与原来的一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
a = [1, 2, 3]
b = copy.copy(a)
print(a, id(a), '\n', b, id(b))
"""
[1, 2, 3] 1944176728640
[1, 2, 3] 1944176728512
"""
a[0] = 2
print(a, '\n', b)
"""
[2, 2, 3]
[1, 2, 3]
"""

这是对于“袋子”里装的是不可变数据类型的情况,若袋子里装的是可变数据类型,如嵌套列表:

1
2
3
4
5
6
7
8
a = [[1, 2], 1]
b = copy.copy(a)
a[0][0] = 2
print(a, '\n', b)
"""
[[2, 2], 1]
[[2, 2], 1]
"""

即,里面的袋子还是同一个。这就是浅拷贝(Copy),它只新创造最外层的“袋子”,但是里面的内容完全不动:引用不可变类型的变量仍引用同一个不可变对象,引用可变类型的变量仍引用同一个“袋子”。

深拷贝(Deepcopy)

对于不可变类型的深浅拷贝,其效果相同,此处不再赘述。

可变类型的深拷贝

当可变类型“袋子”中只有不可变类型的数据时,深拷贝与浅拷贝的效果相同。但是当可变类型“袋子”中也有可变类型时,深拷贝会对“袋子”里面的所有可变类型变量递归地使用深拷贝。也就是说,深拷贝后的两个变量ab尽管会引用相同的不可变类型对象,但是它们是完全不同的两个变量,修改a完全不会影响b

1
2
3
4
5
6
7
8
a = [[1, 2], 1]
b = copy.deepcopy(a)
a[0][0] = 2
print(a, '\n', b)
"""
[[2, 2], 1]
[[1, 2], 1]
"""

元组的深浅拷贝

元组本身是不可变的数据类型,因此,它的深浅拷贝应该是一样的。但是,当元组内含有可变类型的对象时,对元组的深拷贝会递归地对该可变类型的对象进行深拷贝,进而导致整个元组被放在新开辟的空间:

1
2
3
4
5
6
7
8
9
tup = (1, [1, 2])
tup1 = copy.copy(tup)
tup2 = copy.deepcopy(tup)
print(id(tup), '\n', id(tup1), '\n', id(tup2))
"""
1944176723264
1944176723264
1944176723328
"""

参考