🕳️ Python 深拷贝 vs 浅拷贝 · 10 连坑

65 阅读2分钟

不是“八股”,是真实代码里踩过的血与泪——
=copy()deepcopy()

微信图片_20251014151033_10_20.jpg

① 一句话原理:3 级拷贝金字塔

级别写法拷贝深度可变共享?
赋值 =b = a0(共享对象)
浅拷贝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.deepcopymemo 参数


⑥ 坑点 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={})
NumPyarr.copy()
pandasdf.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,深拷保平安!”**🎵