Python的浅拷贝把我坑惨了,原来copy()和deepcopy()的区别这么大

简介: 本文以一个因浅拷贝导致权限配置互相污染的Bug为引,深入解析Python中赋值、浅拷贝与深拷贝的本质区别:赋值是引用共享,浅拷贝仅复制顶层容器(内层对象仍共享),深拷贝则递归创建完全独立副本。结合代码实例与形象比喻,厘清适用场景及性能陷阱,助开发者避坑提效。(239字)

一个让我加了两天班的Bug

去年我接手了一个用户权限管理模块。每个用户登录时,系统会基于一个“默认权限模板”生成一份独立的权限配置。

代码大概长这样:

default_permission = {
   "admin": False,
   "modules": ["dashboard", "profile"],
   "settings": {"theme": "light", "notifications": True}
}

def create_user_permission(username):
   user_perm = default_permission.copy()
   user_perm["username"] = username
   return user_perm

alice = create_user_permission("alice")
bob = create_user_permission("bob")

逻辑看起来没问题,对吧?default_permission.copy()复制了一份,Alice和Bob各用各的。

直到测试同事跑来告诉我:“我给Alice关了通知提醒,Bob的通知提醒也跟着关了!”

我一脸懵:“怎么可能?Alice和Bob是两份独立的权限配置啊!”

查了半天才发现问题出在这行代码上:default_permission.copy()

这个copy(),只复制了最外面那层字典。里面的settings字典还是同一个——Alice和Bob的settings指向的是同一块内存地址。我改了Alice的settings,Bob的settings也跟着变了。

那天下午,我把Python拷贝的三种方式彻底研究了一遍。今天把这些坑讲清楚,希望你别重蹈我的覆辙。

代理 IP 使用小技巧 让你的数据抓取效率翻倍 (45).png

第一步:赋值不是拷贝

很多人搞不清楚“赋值”和“拷贝”的区别。

a = [1, 2, [3, 4]]
b = a          # 这不是拷贝,是贴标签
b.append(5)
print(a)       # [1, 2, [3, 4], 5] —— a也跟着变了

在Python里,b = a不是复制,而是把标签b也贴到a指向的那个对象上。ab指向同一个对象,改b就是改a

你可以用id()验证一下:

print(id(a))  # 比如 140234567890
print(id(b))  # 同样是 140234567890

两个变量指向同一个内存地址,本质上就是一个东西。

要得到一份“独立”的副本,需要用到拷贝操作——copy()copy.deepcopy()。但要注意:不是所有拷贝都能得到完全独立的对象

第二步:浅拷贝——只复制第一层

Python里的“拷贝”分两种:浅拷贝和深拷贝。

import copy

original = [[1, 2], [3, 4]]
shallow = original.copy()        # 浅拷贝
# 或者 shallow = copy.copy(original)

copy()只复制了最外面那层容器,里面的元素还是原来那些对象的引用。

画个图理解一下:

  • original指向一个列表对象A
  • shallow指向一个新列表对象B
  • B里的两个元素,指向的依然是A里的那两个子列表

所以当你修改B的外层结构(比如shallow.append(5)),A不受影响。但当你修改B里的内层列表(比如shallow[0].append(99)),A里对应的内层列表也会变,因为它们是同一个对象。

original = [[1, 2], [3, 4]]
shallow = original.copy()

shallow[0].append(99)
print(original[0])  # [1, 2, 99] —— 原件被改了!
print(shallow[0])   # [1, 2, 99]

这就是我那个权限系统翻车的原因——default_permission.copy()只复制了最外层字典,里面的settings字典还是同一个。

default = {"settings": {"theme": "light"}}
copy = default.copy()
copy["settings"]["theme"] = "dark"
print(default["settings"]["theme"])  # "dark" —— 原件被改了!

浅拷贝和深拷贝的区别,只与复合对象有关——也就是包含其他对象的对象,比如列表、字典、类的实例。对于数字、字符串这类“原子”类型,没有拷贝一说,产生的都是原对象的引用。

第三步:深拷贝——彻底独立

深拷贝会递归地复制对象里的所有层级,每一层都是全新的独立对象。

import copy

original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)

deep[0].append(99)
print(original[0])  # [1, 2] —— 不受影响
print(deep[0])      # [1, 2, 99]

深拷贝的规则是:遇到一个对象就复制一个,遇到嵌套的对象就继续递归复制,直到所有东西都是全新的。

官方文档的解释更精准:浅拷贝构造一个新的复合对象,然后把原始对象中找到的对象的引用插进去;深拷贝构造一个新的复合对象,然后递归地把原始对象里找到的对象的副本插进去。

用我那个权限系统的例子:

import copy

default_permission = {
   "admin": False,
   "settings": {"theme": "light", "notifications": True}
}

# 用深拷贝代替浅拷贝
user_perm = copy.deepcopy(default_permission)
user_perm["settings"]["notifications"] = False

print(default_permission["settings"]["notifications"])  # True —— 不受影响

深拷贝出来的对象就是完完全全的新对象,不管是对象本身还是对象中包含的子对象,都和原始对象不一样。浅拷贝出来的对象则是“外新内旧”——对象本身是新的,但子对象还是原来的。

第四步:什么时候用哪个?

用浅拷贝的场景:

  • 数据结构是单层的(没有嵌套),比如[1, 2, 3]
  • 你明确知道嵌套对象不需要独立,共享反而更好
  • 对性能敏感,浅拷贝比深拷贝快得多

用深拷贝的场景:

  • 数据结构有嵌套(列表套列表、字典套字典)
  • 你需要在副本上修改嵌套对象,但不想影响原始数据
  • 多线程环境下传递数据,需要确保线程间数据隔离
  • 用户配置、订单数据、模板生成这类场景

性能方面要注意: 深拷贝比浅拷贝慢得多。有测试显示,深拷贝比浅拷贝慢约15倍。对于深层嵌套的结构,甚至可能慢100倍。所以别盲目用深拷贝,该用浅拷贝的时候就用浅拷贝。

第五步:还有几个坑

坑一:循环引用

如果对象自己引用自己,深拷贝可能会出问题:

a = []
a.append(a)  # 自己引用自己
# copy.deepcopy(a)  # 可能报 RecursionError

不过Python的deepcopy()内部用了memo字典来记录已复制的对象,能处理大部分循环引用的情况。

坑二:不可变对象

对于不可变对象(数字、字符串、元组),浅拷贝和深拷贝没啥区别:

a = 5
b = copy.copy(a)
c = copy.deepcopy(a)
# b和c就是5,跟a没关系

坑三:自定义对象

如果你的类里有嵌套结构,记得实现__copy____deepcopy__方法来控制拷贝行为。

总结

一句话记住区别:浅拷贝只复制表面,里面的东西还是共享的;深拷贝从头到尾全部复制,完全独立

用我翻车的那个权限系统打个比方:

  • 赋值(b = a :给同一本书贴两个标签,撕掉一个另一个还在
  • 浅拷贝(copy() :复印了书的封面,但里面的每一页还是原来那本
  • 深拷贝(deepcopy() :把整本书从头到尾复印了一遍,崭新的一本

现在我写代码但凡遇到嵌套数据结构要复制,都会停下来想一想:我到底要的是“复印封面”还是“复印全书”?想清楚再动手,少加两天班。

目录
相关文章
|
1天前
|
人工智能 安全 测试技术
|
3天前
|
云安全 人工智能 安全
阿里云 Agentic SOC 位居 IDC MarketScape安全运营智能体2026领导者类别
以 Agentic AI 重构安全运营闭环,阿里云云安全在产品能力与市场份额
1151 3
|
2天前
|
数据采集 机器学习/深度学习 人工智能
田间杂草定位与检测4200张YOLO智慧农业数据集分享
本数据集含4200张真实农田图像,YOLO格式,单类别(杂草)高质量标注,覆盖多作物、多光照、多生长阶段等复杂场景,专为智慧农业杂草检测与智能除草设备研发设计,支持YOLOv5/v8/v10等主流模型训练。
344 93
|
3天前
|
缓存 UED 开发者
Codex109天重置23次,明天还要再送一次
Codex近109天完成23次额度重置,7月14日将迎来第24次。Tibo高频响应用户反馈:优化GPT-5.6高消耗问题、补发失效福利、调整重置时间——形成“反馈→回应→修复→补偿”正向闭环,彰显以用户为中心的产品哲学。(239字)
548 11
|
7天前
|
存储 人工智能 JSON
Qwen 本地部署搭配 ComfyUI 生成 AI 漫剧完整实操指南(小白零基础可落地,零成本无限生成+角色一致性天花板)
2026全网最优本地漫剧流水线:零成本、离线运行、角色统一、低配(8G显卡)可跑。融合Qwen本地大模型+ComfyUI双引擎,实现剧本生成→分镜绘图→动态成片全自动,隐私安全、无审核限流,新手30分钟上手,日更无忧。(239字)
|
7天前
|
人工智能 缓存 JSON
刚刚 GPT-5.6 发布,吊打 Claude 5 和 Grok 4.5?一手实测来啦!
GPT-5.6 刚发布,跑分号称超越 Fable 5,真的假的?附一手实测,让 3 个最强 AI 编程模型 GPT-5.6 Sol、Claude Fable 5、Grok 4.5 在 Cursor 里同时一把梭开发网页游戏,看看最新模型到底谁更能打。
359 0

热门文章

最新文章