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

发布时间:2026/9/15 11:48:50

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/9/13 9:45:43

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

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

2026/9/12 21:19:32

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

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

2026/9/15 11:47:25

vDisk技术结合VOI/IDV架构在考场信息化中的应用

1. 考场网络部署的痛点与挑战考场信息化建设一直是教育行业数字化转型的重点场景。传统PC考场在运维管理上面临着诸多难题:考试软件安装复杂、系统镜像分发困难、终端设备维护成本高、考试环境一致性难以保障。特别是在大规模考试期间,动辄数百台终端需要…

2026/9/15 11:47:25

芯片型号命名规则解析:从字母数字看懂选型与替换关键

1. 为什么芯片型号不是一串随机字母数字,而是一本需要破译的“行业密电码”刚入行那会儿,我盯着一块ST的STM32F103C8T6开发板发呆——这串字符里,“STM”是公司缩写,“32”代表32位架构,“F”指Flash型,“1…

2026/9/15 11:42:24

2020 Linux桌面生产力应用实战指南

1. 项目概述:为什么2020年还要专门谈Linux桌面生产力应用?“10个应用程序让您的Linux桌面更具生产力”——这个标题乍看平平无奇,但放在2020年这个时间节点上,它其实是一份带着时代烙印的务实指南。那一年,统信UOS、麒…

2026/9/15 4:54:30

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/15 0:01:16

AI英语单词APP开发:自适应学习算法与移动端优化实践

1. 项目概述 作为一名在移动应用开发领域摸爬滚打多年的老手,我最近完成了一个AI英语单词APP的开发项目。这个项目将传统单词记忆方法与现代AI技术相结合,打造了一款能够智能适应不同用户学习习惯的英语学习工具。 市面上大多数单词APP都存在一个通病&a…

2026/9/15 0:01:16

Flutter与OpenHarmony结合开发手语学习APP实战

1. 项目背景与核心价值作为一名同时接触过Flutter和OpenHarmony的开发者,最近我完成了一个基于Flutter for OpenHarmony的手语学习APP实战项目。这个项目最大的特点在于实现了跨平台框架与国产操作系统深度结合的创新实践——用Flutter开发的应用能完美运行在OpenHa…

2026/9/15 0:01:16

六个月成为机器人工程师:从ROS2到SLAM的实战路径

1. 六个月的紧迫感从哪来:先搞清楚你要成为哪种机器人工程师说实话,六个月的期限并不是一个宽松的时间线。市面上任何一本正经的机器人学教材都超过五百页,ROS2的官方文档可以翻到你怀疑人生,再加上ABB、KUKA这些工业机器人厂家动…

2026/9/14 11:59:31

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/14 13:53:59

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/15 11:42:23

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

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

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

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