Python中的浅拷贝和深拷贝

这个问题问得特别到位!浅拷贝和深拷贝是Python里一个"看似简单,但一写代码就懵"的核心概念。很多老手都在这里踩过坑。

我用一个特别生活化的场景给你讲明白,保证一听就懂!


1. 先看一个"悲剧"故事(引子)

假设你有一个嵌套列表(列表里套列表):

原版菜单 = ['汤', ['鱼', '肉'], '甜点']

现在你想给朋友拷贝一份菜单,让他随便改,不影响你的原版。

结果你用了最直接的方式:

朋友的菜单 = 原版菜单  # 这是赋值,不是拷贝!
朋友的菜单[1][0] = '素鸡'  # 把鱼改成素鸡

print(原版菜单)  
# 输出:['汤', ['素鸡', '肉'], '甜点']  → 你的原版也被改了!😱

为什么? 因为=只是给同一个对象贴了个新标签,根本没复制。这就是引用赋值


2. 浅拷贝:只Copy第一层

浅拷贝(Shallow Copy):就像你复印一份文件,只复印了第一页的目录,目录里写的"第二章内容详见第50页",但第50页的内容还是共用同一份。

Python里怎么实现?

import copy

原版 = ['汤', ['鱼', '肉'], '甜点']
浅拷贝版 = copy.copy(原版)  # 或者用 原版.copy() 或 原版[:]

看看效果:

浅拷贝版[0] = '粥'           # 改第一层(字符串)→ 完全OK,互不影响
浅拷贝版[1][0] = '素鸡'      # 改第二层(嵌套列表)→ 问题来了!

print(原版)      # ['汤', ['素鸡', '肉'], '甜点'] → 原版被影响了!
print(浅拷贝版)  # ['粥', ['素鸡', '肉'], '甜点']

图解浅拷贝(灵魂画手上线):

原版 -----> [ '汤',  [地址A], '甜点' ]
                      |
浅拷贝版 --> [ '汤',  [地址A], '甜点' ]
                      
注意:两个列表的[1]位置指向的是同一个地址A(嵌套列表)
所以你改地址A里的内容,两个都变!

3. 深拷贝:彻底分家,各过各的

深拷贝(Deep Copy):就像从头到尾全部重新抄写一遍,连书里引用的附录都重新抄一份。两份完全独立,互不干扰。

Python里怎么实现?

import copy

原版 = ['汤', ['鱼', '肉'], '甜点']
深拷贝版 = copy.deepcopy(原版)

看看效果(这次终于安全了):

深拷贝版[0] = '粥'           
深拷贝版[1][0] = '素鸡'      # 随便改!

print(原版)      # ['汤', ['鱼', '肉'], '甜点'] → 纹丝不动!✅
print(深拷贝版)  # ['粥', ['素鸡', '肉'], '甜点']

图解深拷贝:

原版 -----> [ '汤',  [地址A], '甜点' ]
                      |
深拷贝版 --> [ '汤',  [地址B], '甜点' ]
                      
注意:Python重新创建了一个全新的嵌套列表(地址B)
两份完全独立,你改你的,我改我的!

4. 核心结论(记小本本)

操作方式 是否拷贝嵌套对象 适用场景
赋值 = 不拷贝,直接引用 只是想给对象起个别名
浅拷贝 .copy() / copy.copy() 只拷贝第一层,嵌套的仍共享 对象结构简单,没有嵌套,或嵌套对象不可变(如字符串、元组)
深拷贝 copy.deepcopy() 递归拷贝所有层,完全独立 对象结构复杂,有嵌套列表/字典,且需要完全独立修改

5. 一个容易踩的坑(必看!)

如果嵌套对象是"不可变类型"(如字符串、元组),浅拷贝就够用了,因为不可变类型没法改内容,只能重新赋值。

原版 = ['汤', '肉', '甜点']  # 注意:没有嵌套列表了!
浅拷贝版 = 原版.copy()

浅拷贝版[1] = '素鸡'   # 这是重新赋值,不是修改原对象
print(原版)   # ['汤', '肉', '甜点'] → 没事!因为字符串不可变

判断技巧:只要你用 对象[索引] = 新值 这种赋值操作,就是在替换引用;只有用 对象[索引][子索引] = 新值 这种链式修改,才会影响共享的嵌套对象。


6. 什么时候用深拷贝?(实战场景)

  • 缓存数据:你从数据库查出一份数据,想缓存一份快照,同时另一份拿去给业务逻辑修改。
  • 多线程/多进程:多个线程要独立修改同一份初始数据。
  • 游戏开发:一个角色模板,要复制出多个独立属性的副本(每个角色有自己的装备列表)。

7. 给你的小作业(动手试试!)

import copy

a = [1, 2, [3, 4]]
b = a.copy()        # 浅拷贝
c = copy.deepcopy(a) # 深拷贝

# 请问:执行下面这行后,a、b、c分别是什么?
b[2][0] = 999

# 答案:a和b都变成 [1, 2, [999, 4]],c保持不变 [1, 2, [3, 4]]

你可以把这段代码敲进编辑器跑一下,亲眼看看结果。亲眼看到效果,比背一百遍概念都管用!


如果还有疑问,比如"什么时候浅拷贝反而更高效"或者"自定义类怎么实现深拷贝",随时来问!这种底层概念搞懂了,后面看源码、写框架都会轻松很多。加油!💪

posted on 2026-08-17 17:52  有点懒惰的大青年  阅读(14)  评论(0)    收藏  举报