>>>PyPathPython 学习站
首页›进阶篇›kp-028
进阶篇 · Advanced lv.3 进阶 kp-028

深浅拷贝与对象相等性

前置知识:kp-003、kp-004、kp-005

1. 一句话定义

浅拷贝只复制最外层容器(内层对象仍共享),深拷贝递归复制全部嵌套结构;== 比值、is 比身份——四者共同构成 Python 的"复制与相等"语义。

2. 为什么重要

这是 kp-003(名字绑定)的进阶篇:几乎所有"改了 A、B 也跟着变"的灵异 bug,根源都在浅拷贝误解或 is/== 混用。

3. 前置知识

kp-003、kp-005(is 与 ==)。

4. 核心概念

python
import copy

a = [[1, 2], [3, 4]]
b1 = a.copy()                 # 浅拷贝(等价 list(a) / a[:] / list(a))
b2 = copy.deepcopy(a)         # 深拷贝

a[0].append(9)
b1[0]        # [1, 2, 9]  ← 内层共享,被波及
b2[0]        # [1, 2]     ← 完全独立

浅拷贝三兄弟等价:a.copy()、list(a)、a[:]——都只复制一层。

5. 原理与机制

text
a ──► 外层[ ] ──► [1,2]     b1(浅) ──► 新外层[ ] ──► 同一个 [1,2] ✗共享
        └────► [3,4]       b2(深) ──► 新外层[ ] ──► 新 [1,2]、新 [3,4] ✓独立

相等性三层:

python
x = [1, 2]; y = [1, 2]
x == y        # True   (__eq__:值相等)
x is y        # False  (身份不同)
x is x        # True

# 自定义类:__eq__ 决定 ==,__hash__ 决定能否进 set/dict
# 不定义 __eq__ 时默认按身份比较(object.__eq__ 即 is 语义)

哈希联动铁律:a == b 必须推出 hash(a) == hash(b);frozen dataclass / NamedTuple / tuple 自动满足,手写类要自己保证(kp-019)。

6. 关键事实(模型/图示)

text
选择决策树:
  需要"新容器、内层独立"? → copy.deepcopy(慢,注意循环引用已内置处理)
  只需新外壳、内层本就不可变? → 浅拷贝(快,够用)
  只是多一个名字? → b = a(kp-003)

7. 直观类比

浅拷贝是复印了文件夹封面:里面每份文件(内层对象)还是原件,谁改谁都看得见;深拷贝是连文件带附件整套复印,从此两套互不相干。

8. 实例与案例

python
# 经典 bug:用浅拷贝"备份"配置
defaults = {"theme": {"dark": True}, "lang": "zh"}
user = defaults.copy()
user["lang"] = "en"            # 安全(顶层新键)
user["theme"]["dark"] = False  # 灾难!defaults["theme"]["dark"] 也变了
# 修复:copy.deepcopy(defaults) 或 dict(config) 的专深拷贝写法

9. 常见误区

  1. b = a 以为有拷贝发生 —— 只是名字绑定(kp-003);要独立从 copy() 开始。
  2. 浅拷贝用于嵌套结构 —— 上例;嵌套可变内容必须 deepcopy。
  3. 在自定义类里定义 __eq__ 却忘了 __hash__ —— 实例从此不可哈希(进不了 set)。
  4. deepcopy 全依赖 —— deepcopy 慢且对含锁/连接的对象不安全;考虑不可变数据结构或显式重建。

10. 自测题

  1. a = [[1]]; b = a[:],执行 b[0].append(2) 后 a 是什么?
  2. x == y 为 True 时 x is y 一定为 False 吗?举例反例。
  3. 为什么可变默认值陷阱(kp-007)是浅拷贝问题的近亲?
参考答案
  1. [[1, 2]]——浅拷贝共享内层列表。
  2. 不一定:小整数、被缓存的短字符串等"同一对象"既 == 也 is(如 a=256; b=256 时 a is b 为 True,是实现细节)。
  3. 都源于"多个名字共享同一个可变对象":默认值跨调用共享、浅拷贝跨嵌套共享。

11. 与其他知识点的关系

  • kp-003 名字绑定:本文是其机制细化。
  • kp-019 魔术方法:__eq__/__hash__ 的联动规则。
  • kp-033 内存:deepcopy 的成本与 GC。

12. 延伸阅读

  • copy 模块文档:https://docs.python.org/zh-cn/3/library/copy.html