不是“八股”,是真实代码里踩过的血与泪——
从=到copy()到deepcopy(),
① 一句话原理:3 级拷贝金字塔
| 级别 | 写法 | 拷贝深度 | 可变共享? |
|---|---|---|---|
赋值 = | b = a | 0(共享对象) | ✅ |
| 浅拷贝 | copy(a) / a[:] / list(a) | 1 层 | 嵌套元素共享 ✅ |
| 深拷贝 | deepcopy(a) | ∞ 递归 | ❌(完全独立) |
口诀:**“赋值共享,浅拷外壳,深拷全套”**🎵
② 坑点 1:赋值 = 共享对象💣
a = [1, [2, 3]]
b = a # 赋值
b[0] = 999
b[1][0] = 888
print(a) # [999, [888, 3]] ← 原列表被改!
解毒:只要嵌套可变结构,就别用
=
③ 坑点 2:浅拷贝只拷“外壳”🧅
import copy
a = [1, [2, 3]]
b = copy.copy(a)
b[0] = 999
b[1][0] = 888
print(a) # [1, [888, 3]] ← 嵌套列表仍共享!
图解:外壳新,内层旧 → 嵌套元素地址相同
④ 坑点 3:切片/工厂函数也是浅📐
a = [[1, 2], [3, 4]]
b = a[:] # 切片
c = list(a) # 工厂
d = [*a] # 解包
id(b[0]) == id(a[0]) # True ← 内层共享!
结论:一切“单层”操作 = 浅拷贝
⑤ 坑点 4:deepcopy 也有“雷区”⚡
from copy import deepcopy
a = [1, 2]
b = deepcopy(a)
id(b) == id(a) # False → OK
# 雷:循环引用
a.append(a)
c = deepcopy(a) # RecursionError!
解毒:自定义
__deepcopy__或使用copy.deepcopy的memo参数
⑥ 坑点 5:自定义对象默认浅📐
class Bag:
def __init__(self): self.items = []
a = Bag(); a.items.append(1)
b = copy.copy(a)
b.items.append(2)
print(a.items) # [1, 2] ← 共享列表!
解毒:实现
__copy__和__deepcopy__方法
⑦ 坑点 6:immutable 容器“假深拷”🧊
t = (1, [2, 3])
t2 = copy.deepcopy(t)
id(t2[1]) == id(t[1]) # True ← 元组不可变,内层仍共享!
结论:元组只拷贝外壳,内层可变元素仍需深拷
⑧ 坑点 7:NumPy 数组特殊拷贝🔬
import numpy as np
a = np.array([1, 2, 3])
b = a.view() # 浅 view
c = a.copy() # 深拷贝
b[0] = 999; print(a[0]) # 999 ← view 共享数据!
口诀:
view()共享,copy()独立
⑨ 坑点 8:pandas DataFrame 视图陷阱🐼
import pandas as pd
df = pd.DataFrame({"a": [1, 2]})
df2 = df[df["a"] > 0] # 返回视图
df2["a"] = 999
print(df["a"].values) # 原表被改!
解毒:使用
.copy()显式拷贝
df2 = df[df["a"] > 0].copy()
⑩ 万能解毒剂:拷贝 checklist✅
| 场景 | 安全写法 |
|---|---|
| 纯数值列表 | list(old) 或 old[:] |
| 嵌套可变 | copy.deepcopy(old) |
| 自定义对象 | 实现 __copy__ / __deepcopy__ |
| 循环引用 | deepcopy(old, memo={}) |
| NumPy | arr.copy() |
| pandas | df.copy(deep=True) |
🎯 实战:深拷贝调试器(10 行)
import copy, json
def debug_copy(obj):
"""打印对象及其一层子对象的 id"""
print(f"Root id: {id(obj)}")
if hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)):
for i, item in enumerate(obj):
print(f" [{i}] id: {id(item)} -> {type(item)}")
用法:
debug_copy(your_list)→ 一眼看出谁共享谁独立
🏁 一句话口诀(背它!)
“赋值共享,浅拷外壳,深拷全套;
自定义对象要手写,NumPy 用 copy,pandas 深拷贝;
**调试先看 id,深拷保平安!”**🎵