news 2026/5/20 22:22:44

Python 浅拷贝与深拷贝:为什么我改了 b,a 也跟着变了?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python 浅拷贝与深拷贝:为什么我改了 b,a 也跟着变了?

Python 浅拷贝与深拷贝:为什么我改了 b,a 也跟着变了?

在 Python 中,列表、字典、集合这类对象都属于可变对象。

也正因为它们“可变”,所以在复制数据时,经常会遇到一个非常经典的问题:

明明我改的是b,为什么a也跟着变了?

这背后涉及三个概念:

  • 直接赋值;
  • 浅拷贝;
  • 深拷贝。

本文会从一个简单列表开始,一步步讲清楚它们之间的区别。

1. 直接赋值不是复制

先看一段最普通的代码:

a=[1,2,3]b=aprint(b)

输出结果:

[1, 2, 3]

看起来b好像得到了一个和a一模一样的列表。

于是我们尝试修改b

b[0]=0print(a)

结果是:

[0, 2, 3]

问题来了:我们明明改的是b,为什么a也变了?

原因是:

b = a并没有创建一个新列表,它只是让b也指向了a指向的那个列表对象。

也就是说,ab是两个变量名,但它们指向的是同一块内存里的同一个列表。

可以这样理解:

a ─┐ ├──> [1, 2, 3] b ─┘

所以无论通过a修改,还是通过b修改,操作的都是同一个列表。

2. 使用 copy 创建浅拷贝

如果我们真的想得到一个新的列表,可以使用列表的copy()方法:

a=[1,2,3]b=a.copy()print(b)

输出结果:

[1, 2, 3]

这时再修改b

b[0]=0print(a)

输出结果:

[1, 2, 3]

这一次,a没有受到影响。

这是因为a.copy()创建了一个新的列表对象。

可以理解为:

a ──> [1, 2, 3] b ──> [1, 2, 3]

两个列表的第一层结构已经分开了。

这就是浅拷贝。

3. 浅拷贝只复制第一层

浅拷贝看起来已经解决问题了,但它有一个非常重要的限制:

浅拷贝只复制最外层容器,不会递归复制内部嵌套的可变对象。

来看一个嵌套列表:

a=[1,2,3,[4,5,6,[7,8,9]]]b=a.copy()b[3][1]=9print(a)

输出结果:

[1, 2, 3, [4, 9, 6, [7, 8, 9]]]

这一次,a又变了。

原因是a.copy()只复制了最外层列表。

最外层确实变成了两个不同的列表:

a ──> [1, 2, 3, 内层列表] b ──> [1, 2, 3, 内层列表]

但是第四个元素里的内层列表,仍然是共享的。

更准确地说:

a[3] ─┐ ├──> [4, 5, 6, [7, 8, 9]] b[3] ─┘

所以当我们执行:

b[3][1]=9

修改的是共享的内层列表。

因此a也会看到变化。

这就是浅拷贝最容易踩坑的地方。

4. 使用 deepcopy 创建深拷贝

如果我们希望嵌套结构里的每一层都被真正复制,就需要使用深拷贝。

Python 提供了内置模块copy

importcopy

使用copy.deepcopy()可以创建深拷贝:

importcopy a=[1,2,3,[4,5,6,[7,8,9]]]b=copy.deepcopy(a)b[3][1]=9print(a)

输出结果:

[1, 2, 3, [4, 5, 6, [7, 8, 9]]]

这一次,a没有变化。

原因是深拷贝会递归复制对象内部的嵌套对象。

可以理解为:

a ──> [1, 2, 3, [4, 5, 6, [7, 8, 9]]] b ──> [1, 2, 3, [4, 5, 6, [7, 8, 9]]]

不仅最外层列表是新的,里面嵌套的列表也是新的。

所以修改b的内部元素,不会影响a

5. 直接赋值、浅拷贝、深拷贝对比

可以用一张表总结它们的区别:

操作是否创建新对象是否复制嵌套对象适用场景
b = a只是想多一个变量名引用同一个对象
b = a.copy()是,只复制第一层列表内部没有嵌套可变对象
b = copy.deepcopy(a)是,递归复制数据结构有嵌套,并且希望完全独立

简单记忆:

  • 赋值:同一个对象,两个名字;
  • 浅拷贝:外层新对象,内层可能共享;
  • 深拷贝:外层和内层都尽量创建新对象。

6. 为什么不可变对象看起来没这个问题

如果列表里放的是整数、字符串、元组这类不可变对象,浅拷贝通常不容易暴露问题。

例如:

a=[1,2,3]b=a.copy()b[0]=0

这里修改b[0],并不是把原来的整数1改成0

整数是不可变对象。

这一步真正发生的是:让b[0]指向另一个整数对象0

所以a[0]不会受到影响。

但如果列表里嵌套的是另一个列表、字典、集合这类可变对象,浅拷贝就可能出现共享内部对象的问题。

7. 什么时候用浅拷贝,什么时候用深拷贝

在实际开发中,可以按下面的思路选择:

如果数据结构很简单,比如:

[1,2,3]["Python","Java","Go"]

使用浅拷贝通常就够了:

b=a.copy()

如果数据结构存在嵌套,并且你会修改嵌套对象,比如:

[{"name":"张三","scores":[90,80]},{"name":"李四","scores":[85,88]},]

这时更适合使用深拷贝:

importcopy b=copy.deepcopy(a)

不过也要注意,深拷贝不是越多越好。

因为深拷贝会递归复制对象,数据结构越复杂,开销越大。

如果只是读取数据,不需要修改,就没有必要深拷贝。

8. 小结

本文通过列表示例讲清楚了 Python 中直接赋值、浅拷贝和深拷贝的区别。

核心结论如下:

  • b = a不是复制,而是让两个变量指向同一个对象;
  • a.copy()是浅拷贝,只复制最外层容器;
  • 浅拷贝遇到嵌套可变对象时,内部对象仍然可能共享;
  • copy.deepcopy()是深拷贝,会递归复制内部对象;
  • 数据结构简单时用浅拷贝,嵌套结构需要独立修改时用深拷贝。

如果用一句话总结:

赋值复制的是引用,浅拷贝复制第一层,深拷贝复制整个嵌套结构。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/20 22:22:11

新手必看!采购午休课桌椅5大避坑指南,90%的人都踩过

校内午休政策全面推进,午休课桌椅成为校园、托管机构的刚需采购品。但多数采购者缺乏专业经验,易陷入“只看价格”“盲目追功能”的误区,导致采购的产品无法适配场景、浪费成本,甚至无法保障学生午休安全与舒适。结合多年行业经验…

作者头像 李华
网站建设 2026/5/20 22:22:08

Go语言Benchmark测试:性能基准测试

Go语言Benchmark测试&#xff1a;性能基准测试 1. 基准测试 func BenchmarkStringConcatenation(b *testing.B) {for i : 0; i < b.N; i {s : "hello" "world"_ s} }2. 运行 go test -bench. -benchmem3. 总结 基准测试帮助量化性能改进&#xff0c;是…

作者头像 李华
网站建设 2026/5/20 22:19:12

独立开发者如何利用 Taotoken 管理多个项目的 AI 支出

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 独立开发者如何利用 Taotoken 管理多个项目的 AI 支出 对于独立开发者而言&#xff0c;同时维护多个项目是常态。每个项目可能涉及…

作者头像 李华
网站建设 2026/5/20 22:17:07

手把手教你解决Vivado仿真器UID冲突:自制板卡也能多开调试

FPGA开发者实战指南&#xff1a;突破Vivado仿真器多开限制的完整解决方案 在FPGA开发过程中&#xff0c;仿真器是不可或缺的调试工具。然而&#xff0c;许多硬件爱好者和学生在使用自制仿真器时&#xff0c;常常会遇到一个棘手的问题——当尝试在同一台PC上同时使用多个仿真器时…

作者头像 李华