# Python浅拷贝和深拷贝有什么区别?
面试官问:"Python中浅拷贝和深拷贝有什么区别?"
很多人会答"浅拷贝拷贝一层,深拷贝拷贝所有层",但这个说法不够准确。真正要讲清楚的是:浅拷贝创建新容器但共享内部元素对象,深拷贝递归创建所有层级的新对象;修改浅拷贝的嵌套结构会影响原对象,深拷贝完全独立。
# 简要回答
浅拷贝(Shallow Copy) 创建一个新的容器对象,但容器内的元素仍然是原对象中元素的引用。外层容器是新的,内部元素是共享的。
深拷贝(Deep Copy) 递归创建新对象,包括容器和容器内的所有嵌套对象。原对象和拷贝对象完全独立,修改任何一方都不影响另一方。
核心区别:
- 浅拷贝:新容器,共享元素。修改嵌套可变对象会互相影响。
- 深拷贝:所有对象都是新的。完全独立,互不影响。
常见浅拷贝方式:切片list[:]、工厂函数list()、dict.copy()、copy.copy()。
深拷贝方式:只有copy.deepcopy()。
# 详细回答
# 赋值 vs 浅拷贝 vs 深拷贝
先理解三者的区别:
import copy
original = [[1, 2], [3, 4]]
# 赋值: 不创建新对象,只是新名字指向同一对象
assigned = original
# 浅拷贝: 创建新列表,但内部列表是共享的
shallow = copy.copy(original)
# 或: shallow = original[:]
# 或: shallow = list(original)
# 深拷贝: 递归创建所有层级的新对象
deep = copy.deepcopy(original)
print(original is assigned) # True
print(original is shallow) # False
print(original[0] is shallow[0]) # True (内部列表共享)
print(original is deep) # False
print(original[0] is deep[0]) # False (内部列表也是新的)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 浅拷贝的行为
修改浅拷贝的嵌套对象会影响原对象:
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
# 修改外层列表: 不影响原对象
shallow.append([5, 6])
print(original) # [[1, 2], [3, 4]]
print(shallow) # [[1, 2], [3, 4], [5, 6]]
# 修改内层列表: 影响原对象
shallow[0].append(99)
print(original) # [[1, 2, 99], [3, 4]]
print(shallow) # [[1, 2, 99], [3, 4], [5, 6]]
2
3
4
5
6
7
8
9
10
11
12
13
14
因为original[0]和shallow[0]指向同一个列表对象,修改这个列表会在两边都可见。
# 深拷贝的行为
修改深拷贝的任何层级都不影响原对象:
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
# 修改外层
deep.append([5, 6])
print(original) # [[1, 2], [3, 4]]
# 修改内层
deep[0].append(99)
print(original) # [[1, 2], [3, 4]] 不受影响
print(deep) # [[1, 2, 99], [3, 4], [5, 6]]
2
3
4
5
6
7
8
9
10
11
12
13
original[0]和deep[0]是两个独立的列表对象。

# 常见浅拷贝方式
列表的浅拷贝:
original = [1, [2, 3]]
shallow1 = original[:] # 切片
shallow2 = list(original) # 工厂函数
shallow3 = original.copy() # copy方法
shallow4 = copy.copy(original) # copy模块
# 都是浅拷贝
print(shallow1[1] is original[1]) # True
2
3
4
5
6
7
8
9
字典的浅拷贝:
original = {"a": [1, 2]}
shallow1 = original.copy() # copy方法
shallow2 = dict(original) # 工厂函数
shallow3 = copy.copy(original) # copy模块
# 都是浅拷贝
print(shallow1["a"] is original["a"]) # True
2
3
4
5
6
7
8
集合的浅拷贝:
original = {1, 2, 3}
shallow = original.copy()
2
# 不可变对象的拷贝
对于不可变对象(整数、字符串、元组),浅拷贝和深拷贝都可能返回原对象:
import copy
num = 42
num_copy = copy.copy(num)
print(num is num_copy) # True (不可变对象,不需要拷贝)
text = "hello"
text_copy = copy.deepcopy(text)
print(text is text_copy) # True
# 但嵌套可变对象的元组会被深拷贝
original = ([1, 2], [3, 4])
deep = copy.deepcopy(original)
print(original is deep) # False
print(original[0] is deep[0]) # False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这是优化:不可变对象拷贝后也无法修改,共享引用不会有副作用。
# 处理循环引用
深拷贝可以处理循环引用:
import copy
original = [1, 2]
original.append(original) # 循环引用自己
deep = copy.deepcopy(original)
print(deep[2] is deep) # True (保持了循环引用结构)
print(deep[2] is original) # False (是新对象)
2
3
4
5
6
7
8
deepcopy会跟踪已经拷贝的对象,避免无限递归。
# 自定义拷贝行为
类可以定义__copy__()和__deepcopy__()控制拷贝行为:
import copy
class MyClass:
def __init__(self, value):
self.value = value
def __copy__(self):
print("执行浅拷贝")
return MyClass(self.value)
def __deepcopy__(self, memo):
print("执行深拷贝")
return MyClass(copy.deepcopy(self.value, memo))
obj = MyClass([1, 2])
shallow = copy.copy(obj) # 执行浅拷贝
deep = copy.deepcopy(obj) # 执行深拷贝
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
memo字典用于跟踪已拷贝的对象,处理循环引用。
# 知识拓展
Q1: 什么时候用浅拷贝,什么时候用深拷贝?
- 浅拷贝:内部元素是不可变对象(数字、字符串),或者确实想共享内部对象。性能更好,内存占用更小。
- 深拷贝:需要完全独立的副本,修改拷贝不能影响原对象。嵌套结构包含可变对象(列表、字典)时通常需要深拷贝。
Q2: 为什么字典的.copy()是浅拷贝?
字典的copy()方法只拷贝字典本身,不递归拷贝值对象。这是性能和常见需求的平衡:大多数时候只需要拷贝一层,深拷贝开销大且不总是必需。
Q3: 函数参数传递是拷贝吗?
不是。Python函数参数是对象引用传递,不是拷贝。传入列表时,函数内外的变量指向同一个列表对象:
def modify(lst):
lst.append(4)
data = [1, 2, 3]
modify(data)
print(data) # [1, 2, 3, 4]
2
3
4
5
6
如果需要避免修改原对象,应该在函数内手动拷贝:
def modify(lst):
lst = lst.copy() # 浅拷贝
lst.append(4)
return lst
2
3
4
可以查看Python函数参数是值传递还是引用传递?了解参数传递机制。
Q4: 切片[:]一定是浅拷贝吗?
对于内置容器(列表、元组、字符串),切片创建新对象,但内部元素是共享的,属于浅拷贝:
original = [[1, 2], [3, 4]]
sliced = original[:]
print(original is sliced) # False
print(original[0] is sliced[0]) # True
2
3
4
Q5: 如何拷贝自定义对象?
如果自定义类没有定义__copy__和__deepcopy__,copy模块会:
- 浅拷贝:创建新实例,
__dict__浅拷贝。 - 深拷贝:创建新实例,
__dict__深拷贝。
通常这个默认行为就够用。需要特殊逻辑(如不拷贝某些属性)时才需要自定义。
评论
验证登录状态...