在 Python 中,直接赋值、浅复制和深复制是处理对象和数据结构(如列表、字典等)时的三种基本方式。理解它们的区别对于避免数据篡改和保持数据完整性非常重要。
注意这里说的这几种操作只针对对象和数据结构,而非比如单个数值的非对象数据。
以下是它们的详细讲解:
1. 直接赋值(Direct Assignment)
机制
- 引用相同对象:直接赋值不会创建新对象,而是让新变量指向原始对象的内存地址。两个变量实际引用了同一个对象。
- 内存共享:修改任意一个变量会影响到另一个变量,因为它们共享相同的内存位置。
示例
x = [1, 2, 3]
y = x # 直接赋值
y[0] = 100
print(x) # 输出 [100, 2, 3]
print(y) # 输出 [100, 2, 3]
在这个例子中,x
和 y
指向相同的列表对象,因此修改 y
也会修改 x
。
应用场景
- 适用于需要两个变量同时引用同一个对象的场景。
- 快速共享数据而不需额外开销。
2. 浅复制(Shallow Copy)
机制
- 复制外层对象:浅复制创建一个新的对象,但只复制了原始对象的外层结构。对于嵌套对象(如列表中的列表),它只复制引用,而不是递归复制内部对象。
- 部分独立:新对象的顶层结构独立,但嵌套结构仍然共享。
示例
使用 .copy()
或 copy.copy()
(针对通用对象)
import copy
x = [[1, 2, 3], [4, 5, 6]]
y = x.copy() # 浅复制
y[0][0] = 100
print(x) # 输出 [[100, 2, 3], [4, 5, 6]]
print(y) # 输出 [[100, 2, 3], [4, 5, 6]]
在这个例子中,x
和 y
是不同的外层列表,但共享了内部的列表。因此,修改内部列表的元素会影响两个列表。
图示
x (outer list)
|
|---> [ [1, 2, 3], [4, 5, 6] ] (inner lists)
| |
| |
V V
same as same as
| |
| |
y (outer list)
|
|---> [ [1, 2, 3], [4, 5, 6] ] (inner lists)
应用场景
- 适用于修改顶层结构的同时保持嵌套对象共享的场景。
- 有效降低内存开销,但要小心嵌套对象的副作用。
3. 深复制(Deep Copy)
机制
- 递归复制:深复制创建一个新的对象,同时递归复制所有嵌套对象,确保整个对象树都是独立的。
- 完全独立:新对象完全独立于原始对象,任何级别的更改都不会相互影响。
示例
使用 copy.deepcopy()
import copy
x = [[1, 2, 3], [4, 5, 6]]
y = copy.deepcopy(x) # 深复制
y[0][0] = 100
print(x) # 输出 [[1, 2, 3], [4, 5, 6]]
print(y) # 输出 [[100, 2, 3], [4, 5, 6]]
在这个例子中,x
和 y
是完全独立的对象,修改 y
不会影响 x
。
图示
x (outer list)
|
|---> [ [1, 2, 3], [4, 5, 6] ] (inner lists)
y (outer list)
|
|---> [ [100, 2, 3], [4, 5, 6] ] (inner lists)
应用场景
- 适用于需要完全独立副本的场景。
- 有助于避免嵌套结构中的副作用,但相对开销较大。
总结
概述
- 直接赋值:共享同一对象。
- 浅复制:复制外层对象,但嵌套对象共享。
- 深复制:递归复制整个对象树,完全独立。
比较表
操作类型 | 内存共享 | 顶层结构独立 | 嵌套对象独立 | 内存开销 | 应用场景 |
---|---|---|---|---|---|
直接赋值 | 是 | 否 | 否 | 低 | 简单引用 |
浅复制 | 否 | 是 | 否 | 中等 | 部分独立 |
深复制 | 否 | 是 | 是 | 高 | 完全独立 |
代码示例总结
# 直接赋值
a = [1, 2, 3]
b = a
b[0] = 100
print(a) # [100, 2, 3]
print(b) # [100, 2, 3]
# 浅复制
import copy
c = [[1, 2], [3, 4]]
d = c.copy()
d[0][0] = 100
print(c) # [[100, 2], [3, 4]]
print(d) # [[100, 2], [3, 4]]
# 深复制
e = [[1, 2], [3, 4]]
f = copy.deepcopy(e)
f[0][0] = 100
print(e) # [[1, 2], [3, 4]]
print(f) # [[100, 2], [3, 4]]
这样就清晰地展示了直接赋值、浅复制和深复制的区别以及如何选择使用它们。