在 Python 中,浅拷贝和深拷贝是两种复制对象的方法,它们在处理复杂数据结构(如列表、字典、对象等)时的行为不同。让我们来详细了解这两者的区别以及它们的应用场景。
概念与区别
浅拷贝(Shallow Copy)
概念:浅拷贝创建一个新对象,但不递归复制原对象内的子对象。相反,新对象中包含的子对象是对原对象中子对象的引用。因此,原对象和新对象共享相同的子对象。
使用:在 Python 中,使用 copy 模块中的 copy 函数进行浅拷贝:
import copy
original_list = [[1, 2, 3], [4, 5, 6]]
shallow_copy = copy.copy(original_list)
行为:浅拷贝后的对象是独立的对象,但子对象仍然是原对象中的相同引用。这意味着对新对象中的子对象的修改会影响原对象中的相同子对象。
应用场景:在你希望复制对象,但仍然保持对子对象的引用的情况下,可以使用浅拷贝。这在一些需要共享子对象的情况下可能是有用的。
深拷贝(Deep Copy)
概念:深拷贝创建一个新对象,并递归复制原对象中的所有子对象。因此,新对象与原对象完全独立,任何一方的修改都不会影响另一方。
使用:在 Python 中,使用 copy 模块中的 deepcopy 函数进行深拷贝:
import copy
original_list = [[1, 2, 3], [4, 5, 6]]
deep_copy = copy.deepcopy(original_list)
行为:深拷贝后的对象与原对象完全独立。你可以在新对象中自由修改子对象,而不会影响原对象中的子对象。
应用场景:在你希望创建一个完全独立的对象,而不共享任何子对象的引用的情况下,可以使用深拷贝。这在你希望确保对原对象的独立性和完整性时非常有用。
总结
区别:浅拷贝只复制对象本身,而不复制其内部的子对象。深拷贝递归复制对象及其内部的所有子对象。
应用场景:根据你的需求选择使用浅拷贝或深拷贝。如果你希望对象之间共享子对象的引用,可以使用浅拷贝;如果你希望对象完全独立,可以使用深拷贝。
例如:
import copy
# 示例数据
original_list = [[1, 2, 3], [4, 5, 6]]
# 浅拷贝
shallow_copy = copy.copy(original_list)
# 深拷贝
deep_copy = copy.deepcopy(original_list)
# 修改原列表中的子列表
original_list[0].append(99)
print("原列表:", original_list)
print("浅拷贝:", shallow_copy) # 浅拷贝中的子列表也受影响
print("深拷贝:", deep_copy) # 深拷贝中的子列表不受影响
在这个例子中,你可以看到对原列表中的子列表的修改会影响浅拷贝,但不会影响深拷贝。
应用场景
浅拷贝
浅拷贝在实际应用中非常有用,特别是在需要复制对象,但不需要完全独立的对象副本时。下面是一个用常见的学校班级换了数学老师的例子来展示浅拷贝,展示如何在管理对象引用、共享数据结构以及避免不必要的深拷贝时使用浅拷贝。
import copy
math_teacher = {"name": "AAA"}
# 学生模板
student_a = {
"name": "student_a",
"math_teacher": math_teacher
}
student_b = copy.copy(student_a)
# 浅拷贝出来的对象修改name属性是不会影响class1_student原对象的
student_b["name"] = "student_b"
print("换老师前", student_a, student_b)
# 学生b知道学校更换老师,学生a也会知道
student_b["math_teacher"]["name"] = "BBB"
print("换老师后", student_a, student_b)
# Outputs
# 换老师前 {'name': 'student_a', 'math_teacher': {'name': 'AAA'}} {'name': 'student_b', 'math_teacher': {'name': 'AAA'}}
# 换老师后 {'name': 'student_a', 'math_teacher': {'name': 'BBB'}} {'name': 'student_b', 'math_teacher': {'name': 'BBB'}}
这里可以看到浅拷贝出来的新对象(student_b),将子对象(math_teacher)老师的名字(name)改成“BBB”后,原对象(student_a)的老师名字也从“AAA”变成了“BBB”,说明了对新对象中的子对象的修改会影响原对象中的相同子对象。这里的不用深拷贝的原因,他们的老师是共享对象,改一个地方就可以了,否则每个深拷贝出来的对象都需要重新把子对象老师的名字给重新赋值一遍,增加操作复杂度。
深拷贝在 Python 中也有许多实际应用场景,特别是在需要复制复杂数据结构而不希望子对象之间共享引用的情况下。深拷贝通过递归复制对象及其子对象,确保原对象和副本之间完全独立。
比如某些全局配置是独一份的且不能随意修改的,如果浅拷贝后然后要将数据结构传递给不同的函数或模块使用,可能会产生一些意想不到副作用(可能会被函数/模块修改了影响全局);又或者是在一些数据结构需要备份的时候也会考虑深拷贝。
总结
浅拷贝和深拷贝是python面试中最常问到的问题,也是可变对象和不可变对象概念的延伸。
- 对于不可变对象(如Python中的int、float、str、tuple等),深拷贝和浅拷贝的结果是相同的,因为这些对象本质上是不可改变的,并且在浅拷贝时并不会引用其他对象。
- 对于可变对象(如Python中的list、dict、set等),浅拷贝和深拷贝的行为有很大不同。浅拷贝会导致共享引用,而深拷贝会创建完全独立的副本。