python3从入门到精通(十六): copy模块之深拷贝和浅拷贝

一、基本概念

在Python中,赋值(=)只是让新变量指向同一个对象,并不会创建副本。而拷贝则分为浅拷贝和深拷贝,它们决定了副本与原对象之间的独立程度。

  • 可变对象:列表list、字典dict、集合set(值可以修改,修改后内存地址不变)
  • 不可变对象:数字int/float、字符串str、元组tuple(值不能修改,修改会生成新对象)

二、相同点和不同点

在Python中,浅拷贝(Shallow Copy)深拷贝(Deep Copy) 核心区别是:拷贝后生成的新对象,是否会递归复制嵌套的子对象

相同点:
1、如果是不可变对象,如:字符串、元组。深拷贝和浅拷贝对原拷贝对象都不会有影响
2、如果是可变对象,且不带子对象,如:列表、字典。深拷贝和浅拷贝对原拷贝对象都不会有影响
3、如果是可变对象,且带子对象,修改不是子对象的内容深拷贝和浅拷贝对原拷贝对象都不会有影响

不同点:
如果是可变对象,且带子对象,修改子对象的内容深拷贝不会对原拷贝对象有影响,浅拷贝会修改原拷贝对象内容

三、浅拷贝(Shallow Copy)

创建一个新的对象,但新对象中存储的是原对象中元素的引用(即内存地址)。
如果原对象内部还嵌套了其他可变对象,不会递归拷贝嵌套的子对象,新对象和原对象会共享这些内部对象(子对象共用同一个内存地址)。

常见创建方式:

  • 列表:list.copy()、copy[:]、list(list_original)
  • 字典:dict.copy()
  • 通用:copy.copy(x)

特点:

  • 顶层对象是独立的,修改顶层元素不影响原对象。
  • 修改嵌套的可变对象(如列表中的列表)会影响原对象,因为引用相同(共用内存,修改会互相影响

3.1、拷贝对象是可变对象,没有子对象

import copy
"""
浅拷贝:
只拷贝容器本身,容器中的元素并未拷贝
第一种:
拷贝对象是可变对象,但没有复杂子对象
结论:
原来的值改变,不会影响浅复制后的值
"""
a = [1, 2, 3, 4, 5]
b = copy.copy(a)
b.append(6)
print("【浅拷贝】===可变对象,且不带子对象===改变拷贝后的对象", b)  # [1, 2, 3, 4, 5, 6]
print("【浅拷贝】===可变对象,且不带子对象===原对象", a)  # [1, 2, 3, 4, 5]
print("【结论】: 不带复杂子对象的可变对象【浅拷贝】后的对象修改和原对象没有关系\n")

3.2、拷贝对象是可变对象,有子对象

"""
第二种:
拷贝对象是可变对象,且拷贝对象中含有复杂子对象
① 修改拷贝对象中非复杂子对象的内容
② 修改拷贝对象中复杂子对象的内容
结论:
① 修改拷贝对象中非复杂子对象的内容,不管修改拷贝前还是拷贝后的对象,都不会影响互相影响
② 修改拷贝对象中复杂子对象的内容,同时会修改原来拷贝对象中复杂子对象的值
"""
c = [1, 2, [3, 4, 5]]
d = copy.copy(c)
# 1.修改拷贝对象中不是复杂子对象的内容
d[0] = 2
print("【浅拷贝】===》可变对象===》且带复杂子对象===》修改不是子对象的内容", d)  # [2, 2, [3, 4, 5]]
print("【浅拷贝】===》可变对象===》且带复杂子对象===》原对象", c)  # [1, 2, [3, 4, 5]]
print("【结论】: 带复杂子对象的可变对象【浅拷贝】后【修改非子对象】的内容和原对象没有关系\n")

# 2.修改拷贝对象中复杂子对象的内容
k = copy.copy(c)
k[2][0] = 5
print("【浅拷贝】===》可变对象===》且带复杂子对象===》修改子对象的内容", k)  # [1, 2, [5, 4, 5]]
print("【浅拷贝】===》可变对象===》且带复杂子对象===》原对象", c)  # [1, 2, [5, 4, 5]]
print("【结论】: 带复杂子对象的可变对象【浅拷贝】后【修改子对象】的内容和原对象有关系=====》浅拷贝修改复杂子对象内容,会影响原对象\n")

四、深拷贝(Deep Copy)

创建一个新的对象,并且递归地拷贝原对象内部所有的子对象,得到完全独立的新副本。新对象与原对象没有任何共享,修改任意一方都不会影响另一方

4.1、拷贝对象是可变对象,没有子对象

"""
第一种:拷贝对象是可变对象,但没有复杂子对象
结论:
原来的值改变,不会影响浅复制后的值
"""
# 1.拷贝对象是可变对象
info = [1, 2, 3, 4, 5]
info_copy = copy.deepcopy(info)
info_copy[4] = 88
print("【深拷贝】===》可变对象===》且不带子对象===》改变拷贝后的对象", info_copy)  # [1, 2, 3, 4, 88]
print("【深拷贝】===》可变对象===》且不带子对象===》原对象", info)  # [1, 2, 3, 4, 5]
print("【结论】: 不带复杂子对象的可变对象【深拷贝】后的对象修改和原对象没有关系\n")

4.2、拷贝对象是可变对象,有子对象

"""
第二种:拷贝对象是可变对象,拷贝对象中有复杂子对象
① 修改不是复杂子对象的内容
② 修改复杂子对象的内容
结论:
是否有复杂子对象,深拷贝都无影响
"""
# 1.拷贝对象中含有复杂子对象。修改不是子对象的部分
e = {"name": "zkc", "age": 18, "info": [1, 2, 3, 4, 5]}
f = copy.deepcopy(e)
param = {"age": 20}
f.update(param)
print("【深拷贝】===》可变对象===》且不带子对象===》改变拷贝后的对象", f)  # {'name': 'zkc', 'age': 20, 'info': [1, 2, 3, 4, 5]}
print("【深拷贝】===》可变对象===》且不带子对象===》原对象", e)  # {'name': 'zkc', 'age': 18, 'info': [1, 2, 3, 4, 5]}
print("【结论】: 带复杂子对象的可变对象【深拷贝】后【修改非子对象】的内容和原对象没有关系\n")

# 2.拷贝对象中含有复杂子对象。修改子对象的部分
h = copy.deepcopy(e)
h["info"][0] = 77
print("【深拷贝】===》可变对象===》且带子对象===》改变拷贝后的对象", h)  # {'name': 'zkc', 'age': 18, 'info': [77, 2, 3, 4, 5]}
print("【深拷贝】===》可变对象===》且带子对象===》原对象", e)  # {'name': 'zkc', 'age': 18, 'info': [1, 2, 3, 4, 5]}
print("【结论】: 带复杂子对象的可变对象【深拷贝】后【修改子对象】的内容和原对象没有关系\n")

五、不可变对象的拷贝

5.1、拷贝对象不可变对象,没有子对象

浅拷贝/深拷贝都不会创建新对象,直接返回原对象引用(效率优化)

如果对象中只包含不可变类型(整数、字符串、元组等),浅拷贝和深拷贝效果看起来一样(因为不可变对象无法被修改,共享无害)。但浅拷贝仍然只拷贝引用,深拷贝也不会复制这些不可变对象本身(Python 内部会复用)

a = (1, 2, 3, 4, 5)
b = copy.copy(a)
c = copy.deepcopy(a)
print(id(a), id(b), id(c))
print(id(a) == id(b) == id(c))

5.2、拷贝对象不可变对象,有子对象

  • 浅拷贝:仍返回原对象引用
  • 深拷贝:会创建新对象(递归拷贝可变子对象)
# 浅拷贝
d = (1, 2, 3, [5, 6])  # 元组嵌套列表
f = copy.copy(d)
print(id(d) == id(f))  # True  浅拷贝不变
f[3].append(8)
print("【浅拷贝】===》不可变对象===》且带子对象===》原对象",d)  # (1, 2, 3, [5, 6, 8])
print("【浅拷贝】===》不可变对象===》且带子对象===》改变拷贝后的对象", f)  # (1, 2, 3, [5, 6, 8])

# 深拷贝
a = (1, 2, [3,4])  # 元组嵌套列表
c = copy.deepcopy(a)
c[2].append(5)
print(id(a) == id(c))  # False 深拷贝创建新对象
print("【深拷贝】===》不可变对象===》且带子对象===》原对象",a) # (1, 2, [3, 4])
print("【深拷贝】===》不可变对象===》且带子对象===》改变拷贝后的对象", c)  # (1, 2, [3, 4, 5])

六、自定义类的拷贝控制

class MyClass:
    def __init__(self, values):
        self.values = values

    def __copy__(self):
        # 自定义浅拷贝
        new = MyClass(self.values[:])  # 只拷贝一层
        return new

    def __deepcopy__(self, memo):
        # 自定义深拷贝
        new = MyClass(copy.deepcopy(self.values, memo))
        return new
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一位不知名民工

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值