核心区别一句话概括:
-
copy(浅拷贝):只复制对象本身,而不复制对象内部所引用的子对象。新旧对象共享内部的子对象。 -
deepcopy(深拷贝):复制对象本身以及它内部所有层次引用的子对象,直到最底层。新旧对象完全独立,互不影响。
为了更好地理解,我们通常将数据分为两种类型:
-
不可变对象:数字、字符串、元组等。对于这些类型,
copy和deepcopy的效果几乎没有区别,因为一旦创建就不能修改,复制时可能会直接引用同一个对象。 -
可变对象:列表、字典、集合等。
copy和deepcopy的区别在它们身上表现得淋漓尽致。
详细解释和示例
1. 赋值(Assignment)
首先,要理解“赋值”根本不是拷贝,它只是给现有对象起了一个新名字(绑定了一个新标签)。
original = [1, 2, [3, 4]]
assigned = original # 这只是赋值,不是拷贝
# 修改原列表
original.append(5)
print(original) # 输出: [1, 2, [3, 4], 5]
print(assigned) # 输出: [1, 2, [3, 4], 5] (assigned 也跟着变了)
# 修改原列表中的子列表
original[2].append(99)
print(original) # 输出: [1, 2, [3, 4, 99], 5]
print(assigned) # 输出: [1, 2, [3, 4, 99], 5] (assigned 也跟着变了)
original 和 assigned 指向内存中的同一个列表对象,所以一个的任何改变都会反映到另一个上。
2. 浅拷贝 (copy)
使用 copy模块 的 copy() 函数或可变对象自带的 .copy() 方法。
import copy
original = [1, 2, [3, 4]]
shallow_copied = copy.copy(original) # 创建了一个新的列表对象
# 1. 修改原列表的第一层(添加新元素)
original.append(5)
print(original) # 输出: [1, 2, [3, 4], 5]
print(shallow_copied) # 输出: [1, 2, [3, 4]] (新列表没有受到影响)
# 2. 修改原列表中不可变元素
original[0] = 100
print(original) # 输出: [100, 2, [3, 4], 5]
print(shallow_copied) # 输出: [1, 2, [3, 4]] (新列表没有受到影响)
# 3. 修改原列表中的子对象(可变对象)
original[2].append(99) # 修改双方共享的子列表
print(original) # 输出: [100, 2, [3, 4, 99], 5]
print(shallow_copied) # 输出: [1, 2, [3, 4, 99]] (新列表的子列表也被改变了!)
图解浅拷贝:
original -> [ 1, 2, ● ]
↓
[3, 4]
↑
shallow_copied -> [ 1, 2, ● ]
可以看到,original 和 shallow_copied 是两个不同的列表,但它们内部的第三个元素(指向子列表的引用)是同一个。所以修改这个共享的子列表,两个“父列表”都会看到变化。
3. 深拷贝 (deepcopy)
import copy
original = [1, 2, [3, 4]]
deep_copied = copy.deepcopy(original) # 创建了一个全新的、完全独立的对象树
# 1. 修改原列表的第一层
original.append(5)
print(original) # 输出: [1, 2, [3, 4], 5]
print(deep_copied) # 输出: [1, 2, [3, 4]] (完全不受影响)
# 2. 修改原列表中的子对象
original[2].append(99)
print(original) # 输出: [1, 2, [3, 4, 99], 5]
print(deep_copied) # 输出: [1, 2, [3, 4]] (完全不受影响!子列表也是独立的)
图解深拷贝:
original -> [ 1, 2, ● ]
↓
[3, 4]
deep_copied -> [ 1, 2, ● ]
↓
[3, 4] (这是一个全新的副本)
original 和 deep_copied 以及它们内部的所有可变子对象都是完全独立的,不存在任何共享关系。修改任何一个都不会影响另一个。
总结对比表
| 特性 | 赋值 (=) | 浅拷贝 (copy.copy()) | 深拷贝 (copy.deepcopy()) |
|---|---|---|---|
| 拷贝内容 | 只创建引用 | 拷贝父对象,不拷贝内部子对象 | 拷贝父对象及其所有子对象 |
| 内存地址 | 完全相同 | 父对象地址不同,子对象地址相同 | 父对象和子对象地址都不同 |
| 修改原对象的一层 | 影响新对象 | 不影响新对象 | 不影响新对象 |
| 修改原对象的子对象 | 影响新对象 | 影响新对象 | 不影响新对象 |
| 性能与内存 | 开销最小 | 开销中等 | 开销最大(递归拷贝所有层级) |
| 使用场景 | 需要完全联动的别名 | 对象结构简单,或子对象不可变,或需要共享子对象 | 对象结构复杂,需要完全独立的副本 |
如何选择?
-
如果你的对象内部只有不可变对象(如列表里全是数字或字符串),用
copy和deepcopy效果几乎一样,但copy更快。 -
如果你的对象内部包含其他可变对象(如列表嵌套列表、字典嵌套列表),并且你希望新旧对象完全独立,互不干扰,就必须使用
deepcopy。 -
如果你希望或不介意新旧对象共享内部的可变子对象,可以使用
copy。
517

被折叠的 条评论
为什么被折叠?



