Python 中深拷贝(Deep Copy)和浅拷贝(Shallow Copy)的区别?
在 Python 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)的主要区别在于如何处理对象内部的子对象(nested objects)。
简单来说:
- 浅拷贝:只复制最外层的容器,内部的元素依然是引用(指向原来的对象)。
- 深拷贝:不仅复制最外层的容器,还递归地复制内部所有的元素,完全独立。
为了讲清楚,我们需要先引入一个基准概念:直接赋值。
0. 预备知识:直接赋值 (=)
直接赋值不是拷贝。它只是给同一个对象起了一个别名。
- 现象:
b = a - 结果:
a和b指向内存中完全相同的地址。修改b也就是修改a。
1. 浅拷贝 (Shallow Copy)
浅拷贝会创建一个新的对象(容器),但是这个新对象里的元素,依然是指向原对象中元素的引用。
- 实现方式:
copy.copy(obj)- 列表切片
list[:] - 工厂函数
list(),dict() - 对象自带的
.copy()方法
- 特点:
- 第一层是独立的:如果你修改最外层的数据(例如增加/删除元素),原对象不会受影响。
- 内部嵌套层是共享的:如果你修改内部嵌套的可变对象(例如列表里的列表),原对象会受影响。
代码示例:
python
import copy
# 原列表:包含一个整数和一个子列表
a = [1, [2, 3]]
# 浅拷贝
b = copy.copy(a)
# 或者 b = a[:]
# 或者 b = a.copy()
# 1. 修改最外层元素(安全)
b.append(4)
print(a) # [1, [2, 3]] <- 原列表没变
print(b) # [1, [2, 3], 4] <- 新列表变了
# 2. 修改内部嵌套列表(危险!会影响原对象)
b[1][0] = 999
print(a) # [1, [999, 3]] <- 原列表也被改了!
print(b) # [1, [999, 3], 4]
2. 深拷贝 (Deep Copy)
深拷贝会创建一个新的对象,并且递归地将原对象中的所有元素(包括子元素、子元素的子元素...)都复制一份。
- 实现方式:
copy.deepcopy(obj)
- 特点:
- 新对象和原对象在内存中是完全独立的。
- 无论怎么修改新对象(哪怕是修改最深层的嵌套数据),原对象都不会受影响。
- 缺点:速度比浅拷贝慢,且占用更多内存。
代码示例:
python
import copy
# 原列表
a = [1, [2, 3]]
# 深拷贝
b = copy.deepcopy(a)
# 1. 修改最外层(安全)
b.append(4)
# 2. 修改内部嵌套列表(安全!原对象不受影响)
b[1][0] = 999
print(a) # [1, [2, 3]] <- 原列表完全没变,保持原样
print(b) # [1, [999, 3], 4] <- 只有新列表变了
3. 总结对比表
| 特性 | 直接赋值 (=) |
浅拷贝 (copy.copy) |
深拷贝 (copy.deepcopy) |
|---|---|---|---|
| 新对象创建 | 否 (只是别名) | 是 (仅外壳) | 是 (完全的新个体) |
| 第一层元素 | 共享 | 独立 | 独立 |
| 嵌套子元素 | 共享 | 共享 (指向同一内存) | 独立 (递归复制) |
| 速度 | 最快 | 快 | 较慢 |
| 内存占用 | 最小 | 中等 | 最大 |
4. 特殊情况:不可变对象 (Immutable Objects)
对于不可变对象(如 int, str, tuple),Python 会进行优化。
- 如果元组里只包含不可变对象(例如
(1, 2)),copy和deepcopy通常不会创建新对象,而是直接返回原对象的引用(因为反正你也改不了它,复制没有意义)。 - 但如果元组里包含可变对象(例如
(1, [2, 3])),deepcopy依然会递归复制那个列表。
一句话概括
- 浅拷贝:藕断丝连(外壳换了,里面还连着)。
- 深拷贝:彻底分手(完全独立,互不相干)。