当前位置:首页 > 学习资源 > 讲师博文 > Python浅拷贝与深拷贝有什么不同?

Python浅拷贝与深拷贝有什么不同? 时间:2026-09-16      来源:华清远见

前言:一个让你深夜加班的数据Bug

想象这样一个场景:你在处理一个电商订单系统,需要复制一份订单数据进行测试。你写下了这样的代码:

original_order = {

    "order_id": "ORD001",

    "items": [{"name": "Python书籍", "price": 89.9}, {"name": "机械键盘", "price": 399.0}],

    "status": "pending"

}

test_order = original_order  # 你以为创建了一份副本

test_order["items"][0]["price"] = 79.9  # 修改测试数据

然后你惊讶地发现,原始订单的商品价格也被改成了79.9元!

这个Bug的背后,正是Python中赋值、浅拷贝与深拷贝的区别在作祟。理解这三者的差异,是写出健壮代码的必修课。

一、赋值:不是拷贝,是贴标签

在Python中,赋值语句并不复制对象,而是创建目标和对象之间的绑定关系。变量本质上只是一个指向内存地址的“标签”。

a = [1, 2, [3, 4]]

b = a  # 赋值——只是让b也指向a所指向的对象

print(id(a) == id(b))  # True,两个变量指向同一内存地址

当你修改b时,a也会跟着变化,因为它们根本就是同一个对象。这就像给同一本书贴了两个书签,无论从哪个书签翻开,看到的都是同一本书的内容。

⚠️注意:对于不可变对象(如数字、字符串、元组),赋值后的“修改”实际上是创建了新对象,不会影响原对象。但真正容易出问题的是可变对象(列表、字典、自定义类实例等)。

二、浅拷贝:复制外壳,共享内核

浅拷贝会创建一个新的复合对象,然后将原始对象中找到的对象的引用插入其中。通俗地说:浅拷贝只拷贝第一层。

2.1 实现方式

Python提供了多种浅拷贝方式:

import copy

original = [1, 2, [3, 4]]

# 方式一:copy模块的copy()函数

shallow_1 = copy.copy(original)

# 方式二:列表的copy()方法

shallow_2 = original.copy()

# 方式三:切片操作

shallow_3 = original[:]

# 方式四:类型构造器

shallow_4 = list(original)

2.2 浅拷贝的行为

来看一个具体例子:

import copy

arr = [[1, 2, 3], [4, 5, 6]]

shallow_copied = copy.copy(arr)

# 修改浅拷贝的外层——不影响原对象

shallow_copied.append([7, 8, 9])

print(arr)  # [[1, 2, 3], [4, 5, 6]] —— 不变

# 修改浅拷贝的内层嵌套列表——会影响原对象!

shallow_copied[0].append(3.5)

print(arr)  # [[1, 2, 3, 3.5], [4, 5, 6]] —— 被修改了!

为什么?因为浅拷贝只复制了外层容器,内层的嵌套列表仍然是共享引用。用一张图来理解:

•原始对象:外层列表 → [内层列表A, 内层列表B]

•浅拷贝后:新外层列表 → [内层列表A, 内层列表B](内层列表A和B还是原来的那两个)

这就是所谓“复制表面,共享内核”。

三、深拷贝:完整的平行宇宙

深拷贝通过copy.deepcopy()实现,它会递归地复制对象及其所有子对象,生成一个完全独立的副本。

import copy

arr = [[1, 2, 3], [4, 5, 6]]

deep_copied = copy.deepcopy(arr)

# 修改深拷贝的任何层级,都不会影响原对象

deep_copied[0].append(3.5)

deep_copied.append([7, 8, 9])

print(arr)  # [[1, 2, 3], [4, 5, 6]] —— 纹丝不动!

深拷贝的过程就像用3D打印机完整复制了一栋房子——包括所有的家具和装饰,新房子与原房子在物理上完全隔离。

3.1 深拷贝的注意事项

深拷贝也并非万能,有两个常见问题需要注意:

1.递归对象:如果对象包含对自身的引用,深拷贝可能陷入递归循环。deepcopy()通过维护一个“备忘录”(memo字典)来避免这个问题。

2.不可拷贝的类型:模块、方法、文件对象、套接字、栈帧等类型无法被拷贝。对于这些对象,deepcopy()会直接返回原对象。

四、一张表看懂三种方式的区别

五、完整对比示例

import copy

a = [1, 2, 3, 4, ['a', 'b']]

b = a                    # 赋值

c = copy.copy(a)         # 浅拷贝

d = copy.deepcopy(a)     # 深拷贝

# 修改原对象

a.append(5)

a[4].append('c')

print('a =', a)  # [1, 2, 3, 4, ['a', 'b', 'c'], 5]

print('b =', b)  # [1, 2, 3, 4, ['a', 'b', 'c'], 5] —— 跟着变

print('c =', c)  # [1, 2, 3, 4, ['a', 'b', 'c']] —— 外层独立,内层跟着变

print('d =', d)  # [1, 2, 3, 4, ['a', 'b']] —— 完全独立!

运行结果清晰地展示了三者的差异:

•b和a完全同步

•c的外层独立但内层共享

•d完全独立

六、特殊情况:不可变对象

对于只包含不可变对象(数字、字符串、元组)的复合对象,浅拷贝和深拷贝的行为差异并不明显:

import copy

a = [1, 2, 3, 4]

b = copy.copy(a)

c = copy.deepcopy(a)

# 对于简单列表,浅拷贝和深拷贝效果类似

a.append(5)

print(b)  # [1, 2, 3, 4] —— 不变

print(c)  # [1, 2, 3, 4] —— 不变

但一旦涉及嵌套的可变对象,差异就会显现。

七、实战建议:何时用哪种?

浅拷贝适用场景

•对象只包含不可变子对象(如纯数字列表)

•需要共享嵌套子对象(有意为之的设计)

•追求性能和内存效率

深拷贝适用场景

•对象包含多层嵌套的可变结构

•需要完全独立的副本,避免任何联动修改

•多线程环境下传递数据

•接口自动化测试中确保每组测试数据独立

性能考量

浅拷贝只复制顶层,内存开销小、速度快;深拷贝需要递归复制所有层级,内存开销大、速度慢。在性能敏感的场景下,优先考虑浅拷贝能否满足需求。

八、总结

1.赋值:只是给同一个对象贴了新标签,不是拷贝。

2.浅拷贝:创建新容器,但嵌套对象仍是共享引用——“各有一栋楼,共用一间房”。

3.深拷贝:递归复制所有层级,完全独立——“各有一栋楼,各有各的房间”。

记住一句话:浅拷贝管一层,深拷贝管到底。理解了这个核心差异,你就能在开发中从容选择合适的拷贝方式,避免那些令人抓狂的“数据联动Bug”。

上一篇:数组指针与指针数组有什么区别?

下一篇:没有了

戳我查看嵌入式每月就业风云榜

点我了解华清远见高校学霸学习秘籍

猜你关心企业是如何评价华清学员的

干货分享
相关新闻
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2026 北京华清远见科技发展有限公司 版权所有 ,京ICP备16055225号-5京公海网安备11010802025203号

回到顶部