Python 的赋值、浅拷贝和深拷贝详解
赋值、浅拷贝和深拷贝是 Python 中经常涉及的概念,也是容易混淆的概念。本文将详细讲解这三个概念的定义、区别和示例说明。
赋值
赋值是将一个对象的引用复制给另一个变量,让它指向同一个对象。例如:
a = [1, 2, 3]
b = a
前面的语句将 [1, 2, 3]
这个列表对象赋值给了 a
变量,而 b
变量则将 a
变量的引用复制一份成为自己的引用。这时候 a
和 b
两个变量都指向同一个列表对象。修改 a
或 b
变量所指向的列表,另一个变量所指向的列表也会发生相应的变化。
a[0] = 0
print(a) # [0, 2, 3]
print(b) # [0, 2, 3]
浅拷贝
浅拷贝是创建一个新的对象,但是新对象作为容器只包括原对象的引用。例如:
import copy
a = [1, 2, [3, 4]]
b = copy.copy(a)
与赋值不同的是,浅拷贝是通过对原对象的某些层次进行复制,创建一个新的对象,并将原对象中的引用复制到新对象中。在这个过程中,新对象与原对象共享内部列表这样的可变对象。当修改内部的可变对象时,两个变量所指向的列表不同,但是它们所包含的对象相同。
a[0] = 0
a[2][0] = 0
print(a) # [0, 2, [0, 4]]
print(b) # [1, 2, [0, 4]]
深拷贝
深拷贝是创建一个新的对象,包括原对象及其所有子对象的拷贝。例如:
import copy
a = [1, 2, [3, 4]]
b = copy.deepcopy(a)
深拷贝与浅拷贝的区别在于对于原对象的子对象,深拷贝将递归地进行子对象的复制,创建一整个新的对象,而浅拷贝仅仅是对子对象内层的引用进行复制,会影响子对象中共享的可变对象。
a[0] = 0
a[2][0] = 0
print(a) # [0, 2, [0, 4]]
print(b) # [1, 2, [3, 4]]
由于深拷贝是递归进行的,当对象的层次很深或者包含循环引用时,深拷贝的性能会变得很低。这时候可以考虑使用其他的方法来减少拷贝的次数。
示例说明
示例一
import copy
a = [1, 2, [3, 4]]
b = a # 赋值
c = copy.copy(a) # 浅拷贝
d = copy.deepcopy(a) # 深拷贝
a[0] = 0
a[2][0] = 0
print(a) # [0, 2, [0, 4]]
print(b) # [0, 2, [0, 4]]
print(c) # [1, 2, [0, 4]]
print(d) # [1, 2, [3, 4]]
在这个示例中,我们创建了一个嵌套列表的对象,并通过赋值、浅拷贝和深拷贝分别创建了三个新的变量。我们修改了 a
变量所指向的列表对象的元素值,即 [1, 2, [3, 4]] -> [0, 2, [0, 4]]
。并且,我们也修改了 a
变量所指向的列表对象中的嵌套列表对象的元素值,即 [3, 4] -> [0, 4]
。我们可以看到,赋值和浅拷贝得到的变量所指向的列表对象同样发生了变化,而深拷贝得到的变量所指向的列表对象不受影响。
示例二
import copy
a = [1, 2]
b = [3, 4]
c = [a, b]
d = [a, b]
e = copy.copy(c)
f = copy.deepcopy(c)
print(c is d) # False
print(c == d) # True
a[0] = 0
print(c) # [[0, 2], [3, 4]]
print(d) # [[1, 2], [3, 4]]
print(e) # [[0, 2], [3, 4]]
print(f) # [[1, 2], [3, 4]]
在这个示例中,我们创建了两个嵌套列表的对象,并将它们组成一个新的嵌套列表对象。我们使用了赋值、浅拷贝和深拷贝分别创建了三个新的嵌套列表对象。我们设置了 c
和 d
变量分别指向同样的嵌套列表对象。我们修改了 a
变量所指向的列表对象的元素值,即 [1, 2] -> [0, 2]
。我们用 is
判断了 c
和 d
变量是否指向同样的列表对象,结果为 False
。我们可以看到,浅拷贝得到的变量所指向的嵌套列表对象虽然不受 内部可变对象 的影响,但是共享同样的父对象;而深拷贝得到的变量所指向的嵌套列表对象完全独立,不受任何对象的影响。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python 的赋值,浅拷贝和深拷贝详解 - Python技术站