page contents

Python中的深拷贝和浅拷贝有什么区别?

那天在公司茶水间,我正拿着杯咖啡刷手机,隔壁组的小陈跑过来跟我说,东哥你面试官太狠了,问了我一个看似简单但越说越乱的问题——“Python 里的深拷贝和浅拷贝有什么区别?”我一听这题笑了,因为这问题我当年也栽过跟头,看起来就像一句话能说清的事,结果一深入就全是坑。

attachments-2025-10-fdHsw6B568eefa16099aa.png那天在公司茶水间,我正拿着杯咖啡刷手机,隔壁组的小陈跑过来跟我说,东哥你面试官太狠了,问了我一个看似简单但越说越乱的问题——“Python 里的深拷贝和浅拷贝有什么区别?”我一听这题笑了,因为这问题我当年也栽过跟头,看起来就像一句话能说清的事,结果一深入就全是坑。

咱今天就顺着这个话题聊聊,别整那些死记硬背的定义,我就用最生活化的方式,带你把这俩兄弟搞明白。

先这么想吧,拷贝,其实就是“复制对象”。比如你有一份名单,想要再拿一份去修改,但又不想改动原件。问题是——Python 在复制的时候,到底是新建了个一模一样的东西,还是只是拿了个“影印件”链接到原来的?这就牵扯出“浅拷贝”和“深拷贝”的区别。

举个例子最直观:

a = [12, [34]]
b = a

你看这行代码,其实根本不是拷贝,而是让 b 指向 a 的同一个对象。你改 b,a 也变,俩人共用一个内存。 比如:

b[0] = 100
print(a)  # [100, 2, [3, 4]]

是不是有点“心连心”的感觉?这就叫“引用赋值”,连浅拷贝都不是。

那“浅拷贝”呢? 浅拷贝是说,我帮你复制最外层的对象,但里面嵌套的内容我可不管,还是指向原来的。 Python 里常见的浅拷贝方式有几种,比如 copy.copy()list.copy()、切片操作 a[:]

来看看这段:

import copy

a = [12, [34]]
b = copy.copy(a)

b[0] = 100
print(a)  # [1, 2, [3, 4]]
print(b)  # [100, 2, [3, 4]]

你看,外层的数字 1 改了没影响到 a,但如果我们改里面的 [3, 4]

b[2][0] = 999
print(a)  # [1, 2, [999, 4]]

好家伙,这下 a 也跟着变了吧?这就是浅拷贝的真相:它只复制了外壳,内部的嵌套对象仍然共用引用。就像你复印了一张文件夹的封面,却没把里面的纸也复印出来。

那“深拷贝”又是啥意思? 你可以理解为“递归拷贝”——不仅把外层复制,连里面嵌套的对象也一并拷贝一份,彻底断绝关系。 Python 用 copy.deepcopy() 实现。

来继续我们的例子:

import copy

a = [12, [34]]
b = copy.deepcopy(a)

b[2][0] = 999
print(a)  # [1, 2, [3, 4]]
print(b)  # [1, 2, [999, 4]]

这回互不影响了,说明 b 真的是一个全新的副本。

你可能会问,那为啥还要分深浅?干脆都深拷贝不就完了吗? 哈哈,这问题我也问过。其实深拷贝的代价不小,尤其是对象里再嵌套对象、对象里再引用自己(循环引用),那一层层递归下去,既耗内存又耗性能。而且 Python 里有时候对象太复杂,deepcopy() 甚至得自己定义拷贝逻辑,不然容易出错。

举个坑例子:

a = []
a.append(a)
b = copy.deepcopy(a)

这时候 a 里包含自己,深拷贝时如果不处理循环引用,会陷入无限递归。幸好 Python 的 deepcopy() 内部做了检测机制,用字典记录访问过的对象,不然你电脑都得卡死。

其实这事儿在工作中挺常见的。那会儿我在做个配置系统,数据结构是字典套列表、列表又套字典。我们需要在运行时动态修改某些配置,但又不能动到全局的原始模板。当时有个新人用 b = a.copy(),结果线上配置改乱了,所有实例都变。后来一查才发现,他只做了浅拷贝,里层的结构都连着原模板。那次之后我们所有的结构化配置都用 deepcopy(),安全但慢一点。

这就引出一个实际的取舍问题:如果数据层次不深,用浅拷贝完全够用,速度快、内存省;但如果是复杂的多层结构,一定要用深拷贝,不然容易出“串数据”的Bug。

再多说一点底层机制的事儿。Python 的对象本质上都是“引用”,当你赋值时只是增加了引用计数,不会真的复制一份。所以 a = b 就是让两个变量指向同一个内存。浅拷贝的时候,会新建一个容器对象(比如新的列表),但容器里装的还是原来的引用;深拷贝才会沿着引用链一路复制下去,直到遇到基本类型(int、str、bool 等不可变对象),它们本身就不会被修改,所以不需要额外复制。

这里有个细节挺多人搞混:字符串、元组这些不可变对象,拷贝再多次也没区别,因为它们根本不会变。比如:

import copy
s = "hello"
a = copy.copy(s)
b = copy.deepcopy(s)
print(a is s, b is s)  # 都是 True

Python 优化了这种情况,直接返回原对象的引用。

有一次我面试一个候选人,我问他这题,他上来就说“浅拷贝只拷贝一层,深拷贝递归拷贝”。我又追问,那你知道浅拷贝对元组有没有用吗?他愣了两秒,说“元组是不可变的,所以不需要拷贝”。 我笑了笑,说,那如果元组里套了个列表呢? 他这才反应过来:“哦,那还是会共享引用。”对的,浅拷贝和深拷贝的区别不是看“类型名字”,而是看“是否有可变对象在里面”。

来看个例子:

import copy
a = (1, [23])
b = copy.copy(a)
b[1][0] = 99
print(a)  # (1, [99, 3])

虽然外层是元组(不可变),但里面的列表是可变的,所以还是被改了。只有 deepcopy() 才能彻底隔离。

说到底,深浅拷贝的核心区别就在于“复制的深度”—— 浅拷贝只复制第一层结构; 深拷贝会复制所有层级的对象,形成完全独立的新副本。

我平时是这么记的: 浅拷贝像“表格复制”,你复制了表格框架,但单元格里放的还是同一个引用; 深拷贝像“整页扫描”,连里面的内容都重新画了一遍。

写到这我又想起个小技巧。其实有时候我们不一定非要用 copy.deepcopy()。如果只是想复制个 JSON 风格的数据(字典、列表、字符串、数值),直接用:

import json
new_data = json.loads(json.dumps(old_data))

这样也能实现类似深拷贝的效果。缺点是速度慢一点,而且如果对象里有自定义类型(比如 datetime),就不太好处理。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2025-10-15 09:34
  • 阅读 ( 38 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1479 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2228 文章
  3. Pack 1479 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章