展开菜单
首页 精品内容 本月促销 装机必备 Windows macOS软件 IOS软件 Android AI PDF教程 专题
全部分类

当前位置:

首页 > 编程开发 > Python深拷贝与浅拷贝的实现及对比详解(很多人都理解错了)

Python深拷贝与浅拷贝的实现及对比详解(很多人都理解错了)

浅拷贝创建新容器,内部元素仍为原对象引用,修改可变子对象会影响副本;深拷贝递归复制所有层级,生成完全独立对象,互不影响。赋值操作仅增加引用而非拷贝。实际应用中应根据嵌套复杂度和隔离需求选择拷贝方式。

前言

在 Python 的世界里,有一道几乎每次面试都会被问到的经典题目:浅拷贝(Shallow Copy)和深拷贝(Deep Copy)到底差在哪?很多开发者,甚至写过几年代码的老手,遇到嵌套结构时也容易翻车——明明只是想复制一份数据,结果改了一个另一个也跟着变了。更让人头疼的是,Bug 出现时根本想不到是拷贝方式的问题。

Python深拷贝与浅拷贝的实现及对比详解(很多人都理解错了)

今天,咱们就把这个话题彻底拆开揉碎,从底层原理到实战避坑,一步到位说清楚。

一、从一个简单的例子开始

先上一段代码,猜猜输出结果是什么:

python
original = [1, 2, [3, 4]]
copy1 = original.copy()          # 浅拷贝
copy2 = original[:]              # 也是浅拷贝

original[2][0] = 99
original[0] = 100

print(original)   # [100, 2, [99, 4]]
print(copy1)      # ???
print(copy2)      # ???

如果你觉得 copy1copy2 都还是 [1, 2, [3, 4]],那你和绝大多数初学者掉进了同一个坑。实际结果是这样的:

python
print(copy1)   # [1, 2, [99, 4]]
print(copy2)   # [1, 2, [99, 4]]

为什么第一个元素没变,内部的列表却跟着改了?答案就藏在“浅拷贝”的本质里。

二、Python 中的对象与引用

在讨论拷贝之前,得先弄清楚 Python 里变量和对象的关系。一句话概括:变量名只是个标签,真正的数据住在内存里的对象上。

python
a = [1, 2, 3]
b = a          # b 和 a 引用同一个对象
b.append(4)
print(a)       # [1, 2, 3, 4]   a 也被改了

b = a 并没有创造新列表,只是让 b 指向 a 指向的那个对象。这种操作叫引用赋值

拷贝则不同——它会新建一个对象,但新对象和原对象的关系取决于复制深度。

三、浅拷贝(Shallow Copy)

1. 什么是浅拷贝

浅拷贝会生成一个新容器,但容器里的每个元素仍然是对原对象中对应元素的引用。用大白话说:外壳换了个新的,但里面的东西还是原来的那批。

2. 浅拷贝的实现方式

Python 提供了好几种写法,本质都是一样的:

  • copy 模块的 copy() 函数:import copy; new_obj = copy.copy(obj)
  • 列表专用:list.copy()list[:]list(list_obj)
  • 字典专用:dict.copy()dict(dict_obj)
  • 集合专用:set.copy()
python
import copy

lst = [1, 2, [3, 4]]
lst_copy = copy.copy(lst)   # 浅拷贝
lst_slice = lst[:]           # 也是浅拷贝
lst_construct = list(lst)    # 浅拷贝

print(lst_copy is lst)       # False,外层对象不同
print(lst_copy[2] is lst[2]) # True,内部列表是同一个

3. 浅拷贝的行为演示

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

original[0] = 100        # 修改外层不可变元素(int)
original[2][0] = 99      # 修改内部可变列表

print(original)   # [100, 2, [99, 4]]
print(shallow)    # [1, 2, [99, 4]]

修改 original[0] 时,整数是不可变对象,shallow[0] 仍然指向原来的整数 1,所以不受影响。

修改 original[2][0] 时,因为 original[2]shallow[2] 指向的是同一个列表对象,所以变动的影子也投到了 shallow 身上。

总结一句话:浅拷贝只管外层是新对象,内部的元素全是原对象的引用。

四、深拷贝(Deep Copy)

1. 什么是深拷贝

深拷贝会把原对象里的所有元素递归地复制一遍,最终得到一个彻彻底底独立的新对象。原对象和新对象之间没有任何共享的引用,你改你的,我改我的,互不干扰。

2. 深拷贝的实现方式

Python 只提供了 copy.deepcopy() 这一种标准做法:

python
import copy

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

print(deep is lst)        # False
print(deep[2] is lst[2])  # False,内部列表也被复制了

3. 深拷贝的行为演示

python
import copy

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

original[0] = 100
original[2][0] = 99

print(original)   # [100, 2, [99, 4]]
print(deep)       # [1, 2, [3, 4]]  完全不受影响

深拷贝实现了真正的“备份”——无论结构多复杂,新的就是新的,旧的还是旧的。

五、深入理解:自定义对象的拷贝

1. 自定义类与 __copy__、__deepcopy__

对于自定义的类,copy.copy()copy.deepcopy() 会优先调用类里定义的 __copy__()__deepcopy__() 方法。如果没定义,默认行为是:copy.copy() 会创建一个新实例并递归浅拷贝所有属性;copy.deepcopy() 则递归深拷贝所有属性。

当然,你也可以手动控制:

python
import copy

class MyClass:
    def __init__(self, data):
        self.data = data

    def __copy__(self):
        # 自定义浅拷贝行为
        new_instance = type(self)(self.data[:])   # 假设 data 是列表
        return new_instance

    def __deepcopy__(self, memo):
        # 自定义深拷贝行为
        new_instance = type(self)(copy.deepcopy(self.data, memo))
        return new_instance

2. 不可变对象的特殊情况

对于 intstrtuple 这类不可变对象,浅拷贝和深拷贝没有本质区别——反正你也改不了它。但注意:如果元组内部包含了可变对象,深拷贝仍然会递归复制那些可变部分。

python
import copy

tup = (1, 2, [3, 4])
tup_copy = copy.copy(tup)
tup_deep = copy.deepcopy(tup)

print(tup_copy is tup)        # True,元组本身不可变,copy 可能返回原对象
print(tup_deep is tup)        # False,deepcopy 会递归复制内部列表
print(tup_deep[2] is tup[2])  # False

六、浅拷贝与深拷贝的对比

特性浅拷贝深拷贝
复制层级只复制最外层对象递归复制所有层级
内存占用小,共享内部对象大,完全独立
速度
与原对象关系内部可变对象会相互影响完全独立,互不影响
适用场景简单数据结构,无需完全隔离复杂嵌套结构,需要完全隔离

七、容易踩的坑

坑1:把 = 当成了拷贝

python
a = [1, 2, 3]
b = a
b.append(4)
print(a)   # [1, 2, 3, 4]   a 也被改了

老生常谈但反复有人犯错——= 只是多贴了一张标签,不是复制数据。

坑2:误以为 copy() 能保护所有层级

python
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0][0] = 99
print(original)  # [[99, 2], [3, 4]]  原数据被改了!

明明用了 copy(),原数据却还是被污染了。原因就是浅拷贝只复制了外层列表,内部子列表依然是引用。

坑3:深拷贝遇到循环引用会不会死循环?

python
import copy

a = []
b = [a]
a.append(b)   # 形成循环引用

c = copy.deepcopy(a)   # 默认可以处理,不会无限递归
print(c)  # [[[...]]]

deepcopy 内部用了一个 memo 字典来记录已经复制过的对象,所以循环引用能安全搞定。

坑4:有些对象根本不能深拷贝

文件对象、线程锁、数据库连接……这些“重量级”对象通常无法复制。如果硬要深拷贝,会抛出异常。解决方案是自定义 __deepcopy__ 方法,返回自身或一个合理的替代品。

python
import copy

f = open('test.txt', 'w')
# copy.deepcopy(f)   # TypeError: cannot pickle '_io.TextIOWrapper' object

八、实际应用场景

场景1:防御性编程

当你把一个对象传给不可信的函数,又不想让函数偷偷改掉你的数据,深拷贝就派上用场了。

python
def process(data):
    # 可能会修改 data,但不想影响外部
    data_local = copy.deepcopy(data)
    # ... 操作 data_local

场景2:缓存与状态保存

实现撤销、重做功能时,需要保留对象的历史快照。深拷贝能保证每个历史版本互不干扰。

python
class Document:
    def __init__(self):
        self.content = []
    def sa ve_state(self):
        return copy.deepcopy(self.content)
    def restore_state(self, state):
        self.content = state

场景3:多线程编程

多个线程要独立处理同一份数据的副本,深拷贝可以避免竞态条件——每个线程拿到的都是自己的副本。

场景4:配置管理

多个模块基于同一份默认配置生成各自的独立配置时,深拷贝防止互相篡改。

九、性能考量与最佳实践

1. 避免不必要的深拷贝

深拷贝可能很慢,尤其是处理大型对象时。只在真正需要隔离时才用。如果只需要复制一层,浅拷贝就够用了。

python
# 如果只需要复制一层,用浅拷贝
new_list = old_list[:]

# 如果内部元素是不可变对象,浅拷贝就够了
nums = [1, 2, 3]
new_nums = nums[:]   # 安全,因为整数不可变

2. 自定义复制方法提高效率
对于自定义类,重写 __copy____deepcopy__ 可以避开不必要的复制(比如共享那些只读的大块数据)。

3. 利用 copyreg 模块注册复制函数
如果遇到第三方库里的类型无法直接复制,可以通过 copyreg 模块注册一个复制函数来解决。

十、总结

1. 浅拷贝:建新壳,壳里的东西还是原来的引用。适合简单结构或不需要完全隔离的场景。

2. 深拷贝:从头到尾复制一遍,新东西和旧东西之间毫无瓜葛。适合复杂嵌套且要求完全隔离的场景。

3. 赋值(=)不是拷贝,只是给同一个对象贴了多个标签。

4. 不可变对象的浅拷贝和深拷贝区别不大,但如果它内部藏了可变对象,深拷贝依然会复制那些可变的部分。

5. 使用 copy 模块时,记住性能开销——深拷贝花了更多时间和内存,别滥用。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发 Python
相关文章 更多
精品专题 更多
本月促销

正软商城本月促销专区,汇集办公、设计、安全、影音、系统工具及AI软件等正版软件优惠活动,提供限时折扣、特价授权和优惠购买信息,活动库存及价格以页面实时展示为准。

装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

IOS软件

正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

AI

正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

PDF教程

正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

Mac软件 更多
灵活计算器
灵活计算器

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

图几
图几

图几是一款适用于 macOS 的截图、标注与美化工具,支持离线操作保障隐私。界面整理和高频系统操作被放到一起考虑,桌面或窗口内容一多时,管理起来会更省心。

密码键盘
密码键盘

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

思源笔记
思源笔记

思源笔记是一款本地笔记软件,提供所见即所得的编辑方式,为长文写作带来顺滑的体验。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

Office 365 简体中文
Office 365 简体中文

一款文字处理软件,一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将 Excel 和 Outlook 等应用与 OneDrive 和 Microsoft Teams 等强大的云服务相结合,Office 365 可让任何人使用任何设备随时随地创建和共享内容。

WALTR PRO
WALTR PRO

WALTR是一款电脑至iOS文件传输转换工具,操作简单,快速实现文件识别与传送。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

CodeExpander
CodeExpander

CodeExpander 是一款快捷短语输入增强工具,通过键入缩写自动展开为自定义文段,提升工作效率。任务管理和过程控制会更完整,持续下载、批量同步或需要稳定传输流程的场景会更适合它。

Mountain Duck
Mountain Duck

Mountain Duck 是一款能将多个网盘挂载到本地的工具,像本地磁盘一样使用网盘。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

Menuist
Menuist

Menuist 是一款面向 macOS 的 Finder 右键菜单增强工具,主要用来补充新建文件、快捷导航等常用操作,让日常文件管理和访问路径时更高效、更顺手。

Mole
Mole

Mole 是一款专为 Mac 设计的深度清理优化工具,涵盖缓存清理、应用管理及实时状态监控等功能。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

WINDOWS 更多
Windows 10
Windows 10

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

思源笔记
思源笔记

思源笔记是一款本地笔记软件,提供所见即所得的编辑方式,为长文写作带来顺滑的体验。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

傲梅轻松备份
傲梅轻松备份

傲梅轻松备份是一款专业易用的数据备份软件,为重要数据提供安全保障。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

Office 365 简体中文
Office 365 简体中文

一款文字处理软件,一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将 Excel 和 Outlook 等应用与 OneDrive 和 Microsoft Teams 等强大的云服务相结合,Office 365 可让任何人使用任何设备随时随地创建和共享内容。

Wise Folder Hider Pro
Wise Folder Hider Pro

Wise Folder Hider Pro 是一款专业级文件和文件夹隐藏加密软件,为私密数据添加多重保护。高频操作更强调就近处理,浏览、整理和跨目录移动文件时,来回切换和重复点击都会少很多。

WALTR PRO
WALTR PRO

WALTR是一款电脑至iOS文件传输转换工具,操作简单,快速实现文件识别与传送。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

CodeExpander
CodeExpander

CodeExpander 是一款快捷短语输入增强工具,通过键入缩写自动展开为自定义文段,提升工作效率。任务管理和过程控制会更完整,持续下载、批量同步或需要稳定传输流程的场景会更适合它。

PinStack
PinStack

PinStack是一款轻量级的Windows平台剪贴板管理工具,优化您的剪贴板使用体验。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

Mountain Duck
Mountain Duck

Mountain Duck 是一款能将多个网盘挂载到本地的工具,像本地磁盘一样使用网盘。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

Seer
Seer

Seer是一款在Win平台下的空格键功能增强效率工具,只需轻敲空格键,就能预览几乎任何格式的文件。它更适合把零散的小功能集中起来使用,处理高频琐碎任务时会更省事。