python中浅拷贝 vs 深拷贝

一、三个概念别混
• 赋值(aliasing):b = a
不拷贝数据,a、b 指向同一个对象。
• 浅拷贝(shallow copy):只复制最外层容器,容器里的元素仍是原来的引用。
常见:copy.copy(x)、x.copy()、list(x)、x[:](列表切片)、dict(x) / set(x)。
• 深拷贝(deep copy):递归复制容器及其所有可达子对象。
常见:copy.deepcopy(x)。

对于不可变对象(int/str/tuple等,且元组内不含可变对象),浅/深拷贝的效果通常一样——因为没必要复制内部内容。

二、最小示例(看得见的差异)

import copy

a = {"nums": [1, 2], "x": 10}

b = a                   # 赋值:同一个对象
c = a.copy()            # 浅拷贝:只拷贝最外层 dict
d = copy.deepcopy(a)    # 深拷贝:递归地全拷

a["nums"].append(3)
a["x"] = 99

print("a:", a)  # {'nums': [1, 2, 3], 'x': 99}
print("b:", b)  # 跟 a 完全一样(同一对象)
print("c:", c)  # 'nums' 跟 a 共享,同步变成 [1,2,3];但 'x' 不会跟着改
print("d:", d)  # 完全独立,仍是 {'nums': [1,2], 'x': 10}

要点:浅拷贝后修改“嵌套里的可变对象”(如列表/字典),会影响原对象;深拷贝不会。

三、常用拷贝方式速查

浅拷贝
• 列表:b = a[:] 或 b = list(a) 或 b = a.copy()
• 字典:b = dict(a) 或 b = a.copy()
• 集合:b = set(a) 或 b = a.copy()
• 通用:import copy; b = copy.copy(a)

深拷贝
• 通用:import copy; b = copy.deepcopy(a)

四、几个容易踩的坑

1)元组“看起来不可变”,但内部可能是可变的


import copy
t1 = (1, [2, 3])
t2 = copy.copy(t1)       # 浅拷贝
t3 = copy.deepcopy(t1)   # 深拷贝

t1[1].append(4)
print(t2)  # (1, [2, 3, 4])  ← 共享了那只列表
print(t3)  # (1, [2, 3])     ← 深拷贝隔离成功

2)自引用/循环引用

deepcopy 使用内部的 memo 字典跟踪已复制对象,可安全处理自引用结构:


import copy
a = []
a.append(a)
b = copy.deepcopy(a)   # 不会无限递归

3)外部资源对象通常不适合深拷贝

比如打开的文件句柄、线程锁、数据库连接……
深拷贝它们没有意义,常常会抛错或只做浅拷贝。此类对象应显式重建或在类中自定义拷贝策略。

4)numpy / pandas 的 .copy() 语义跟 Python 标准库不同
• numpy.ndarray.copy() 会复制数据缓冲(更像“深拷贝数据”),但 view 不是拷贝。
• pandas.DataFrame.copy(deep=True) 是数据级拷贝;从 2.0 起引入 Copy-on-Write,行为更复杂。

要分清:库内的 copy ≠ copy 模块的浅/深拷贝 含义完全一致。

五、自定义类如何控制拷贝?

实现 copydeepcopy(self, memo):


import copy

class Box:
    def __init__(self, items):
        self.items = items

    def __copy__(self):
        # 浅拷贝:复制最外层,内部共享
        cls = self.__class__
        new = cls(self.items)         # 不复制 items
        return new

    def __deepcopy__(self, memo):
        cls = self.__class__
        new = cls(copy.deepcopy(self.items, memo))
        return new

b1 = Box([1,2])
b2 = copy.copy(b1)
b3 = copy.deepcopy(b1)

六、怎么选?
• 数据一层结构或内部全是不可变 → 浅拷贝即可,快且省内存。
• 有嵌套可变对象且需要完全隔离 → 深拷贝。
• 大对象/性能敏感 → 尽量避免深拷贝;考虑设计为不可变数据或显式构造新对象只复制需要变动的部分。

七、可直接运行的小测


import copy

def show(title, obj):
    print(title, obj, "| id(obj):", id(obj))

a = {"xs": [1,2], "y": {"z": 3}}
b = a                  # 赋值
c = a.copy()           # 浅拷贝
d = copy.deepcopy(a)   # 深拷贝

a["xs"].append(9)
a["y"]["z"] = 7

show("a", a)  # xs -> [1,2,9], y.z -> 7
show("b", b)  # 同 a
show("c", c)  # xs 受影响(共享),y.z 也受影响(共享),但 c 本身的 id 不同
show("d", d)  # 完全没受影响

一句话总结:
• 浅拷贝:只复制壳,里头东西共用。
• 深拷贝:把里里外外都复制一份,互不影响。
按数据结构与性能需求选择即可。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MonkeyKing.sun

对你有帮助的话,可以打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值