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

发布时间:2026/11/3 7:27:49

Python的浅拷贝把我坑惨了,原来copy()和deepcopy()的区别这么大 一个让我加了两天班的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的settingsBob的settings也跟着变了。那天下午我把Python拷贝的三种方式彻底研究了一遍。今天把这些坑讲清楚希望你别重蹈我的覆辙。第一步赋值不是拷贝很多人搞不清楚“赋值”和“拷贝”的区别。a [1, 2, [3, 4]] b a # 这不是拷贝是贴标签 b.append(5) print(a) # [1, 2, [3, 4], 5] —— a也跟着变了在Python里b a不是复制而是把标签b也贴到a指向的那个对象上。a和b指向同一个对象改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指向一个列表对象Ashallow指向一个新列表对象BB里的两个元素指向的依然是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()把整本书从头到尾复印了一遍崭新的一本现在我写代码但凡遇到嵌套数据结构要复制都会停下来想一想我到底要的是“复印封面”还是“复印全书”想清楚再动手少加两天班。
延伸阅读

更多相关文章

2026/10/28 23:49:48

2026年AI科研绘图工具横评:5款神器拯救论文党

2026 AI 科研绘图 5 大工具横评|PaperXie 登顶第一,新手零门槛搞定 SCI 配图一、PaperXie(综合评分★★★★★ 排名 No.1)核心优势原生适配 SCI / 国自然绘图规范,配色、字体、分辨率直接符合期刊要求,不用…

2026/11/1 19:32:43

确认流程要问清什么,欧派立方体床垫怎么选才稳

半夜翻身时身体容易陷住,早上起床腰背发僵,很多人第一反应是换一张更硬的床垫。其实,腰酸背痛人群选床垫,重点不是“越硬越好”,而是看软硬度和支撑能不能同时成立。床垫太硬,肩、腰、胯等位置可能贴合不足…

2026/11/3 7:25:14

杭州印刷包装行业数智化转型与TOP5工厂实力解析

1. 2026杭州印刷包装行业数智化转型现状当前杭州印刷包装行业正处于从传统制造向智能制造转型的关键阶段。根据最新行业调研数据显示,2026年杭州地区规模以上包装企业中,实现全流程数字化的企业占比约为35%,较2021年提升了22个百分点。这种转…

2026/11/3 7:25:14

JVM垃圾回收机制:原理、算法与实战调优

1. JVM与垃圾回收机制概述Java虚拟机(JVM)作为Java生态的核心运行时环境,其最引人注目的特性之一就是自动内存管理机制。想象一下,如果每次创建对象后都需要手动释放内存,Java开发者的工作效率将大打折扣。我在实际项目…

2026/11/3 7:25:14

24V车载供电宽压方案:SEPIC前级与TLF35584启动时序设计

简介:这份应用笔记面向汽车电子硬件工程师与电源设计人员,聚焦TLF35584在24V车载系统中耐压不足的痛点,给出可落地的SEPIC电路改造方案。TLF35584管脚耐压仅40V,难以应对5-58V宽供电范围及ISO 16750-2标准下5A负载突降的钳位需求&…

2026/11/3 7:25:14

PyQt5 QApplication 详解:从模板代码到事件循环与工程实践

做桌面工具开发这些年,被问得最多的一类问题不是布局怎么调、信号槽怎么连,而是"我这个脚本跑起来窗口一闪就没了""为什么别人的程序能一直开着,我的执行完就退出"。这类问题的答案,十有八九都指向同一个东西…

2026/11/3 7:25:14

PyQt5 QApplication详解:从启动模板到事件循环与全局管理

1. 从一行代码说起:QApplication到底在干什么很多人第一次写PyQt5程序,都是从网上抄来的那五行模板开始的:导入QApplication、导入QWidget、app QApplication(sys.argv)、window.show()、sys.exit(app.exec_())。跑起来能出窗口,…

2026/11/3 7:20:14

挣值管理EVM核心七参数:PV、EV、AC与SPI、CPI实战解析

做系统集成的老手,大概都经历过这种场面:项目例会上,负责人问“现在花了多少钱、活干到哪了”,项目经理翻出采购单说花了五十多万,翻出施工日志说大概干了一半,紧接着追问“那到底是快了还是慢了、钱是省了…

2026/11/2 6:08:01

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/2 6:39:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

STM32实战:基于MAX14431数字隔离器的SPI隔离通信方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

工业SPI隔离通信设计:MAX14431BAEE+与STM32L4A6RG实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/3 1:04:48

STM32+NRF24L01无线抢答器:硬件设计、通信协议与低功耗实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/2 6:08:01

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/2 6:39:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑