python的深拷贝与浅拷贝

摘要

抽象地说,浅拷贝就像引用类型,而深拷贝就比较像值类型。浅拷贝是指拷贝对象与源对象共用一份实体,仅仅是引用的变量不同,即变量的名称不同,但往深了挖其实是一个东西。对源对象和拷贝对象其中任何一个做改动,都会影响另一个对象。深拷贝是指源对象和拷贝对象相互独立,对其中任一对象做改动都不会对另一对象产生影响。

浅拷贝:只复制源对象的基本类型、对象类型,仍属于原来的引用。即指的是重新分配一块内存,创建一个新的对象,但里面的元素是源对象中各个子对象的引用。
深拷贝:不仅复制对象的基本类,同时也复制源对象中的对象,就是说完全是新对象产生的。重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联。

这么说有点抽象了,下面举个生动的例子吧。相信大家看完就明白了。

举例

  • 浅拷贝

从前,有一个人叫张三,但是家里一般都喊他的小名“狗蛋儿”。今年夏天格外的热,张三和朋友去河里洗澡,正当游得开心的时候,张三的腿突然被水草缠住,溺水而亡,卒 18岁。在张三的追悼会上,张三的同学哭着喊“张三”,十分伤心;张三的家人也哭的天昏地暗,嘴里不停地喊着“狗蛋儿”啊。无论是喊“张三”,还是“狗蛋儿”,都是这个人溺水而亡。“张三”溺水而亡,那么“狗蛋儿”也溺水而亡,反之亦然。这就是浅拷贝的道理。

  • 深拷贝

从前,还是那个人叫张三,后来他觉得张三这个名字不够酷,长大之后给自己娶了一个超酷的名字“李四”,从此他的身份证上名字就是李四,张三已经跟他没有关系了,后来又有一个人取名叫张三(相当于深拷贝)。这天这个后来取名张三的人出了车祸住进了重症监护室,生命岌岌可危,但是这丝毫不影响李四在公司加班到深夜。因为现在的张三和李四是两个不同的人,相互独立,互不影响。

浅拷贝和深拷贝的区别

浅拷贝是指将源对象中的数值类型的字段拷贝到新的对象中,而对于源对象中的引用型字段则只复制它的一个引用到新对象。如果改变新对象的值,那源对象中引用型字段的值也将反映在源对象中。**深拷贝和浅拷贝的不同是对于引用的处理,深拷贝将会在新对象中创建一个全新的和源对象中对应字段相同的字段,也就是说这个引用和原始对象的引用是不同的。我们在改变新对象中的这个字段的时候是不会影响到源对象中对应字段的内容。

代码验证

对数据采用浅拷贝的方式时,如果原对象中的元素不可变,那倒无所谓;但如果元素可变,浅拷贝通常会出现一些问题,例如:

import copy

# list1第一个元素为列表,是可变数据类型; 第二个元素为元组,是不可变类型
list1 = [[1, 2], (30, 40)]
# list1执行浅拷贝,赋值给list2
# 因为浅拷贝里的元素是对原对象元素的引用,因此 list2 中的元素和 list1 指向同一个列表和元组对象。
list2 = copy.copy(list1)
# list1新增一个元素100
list1.append(100)
print("list1:",list1)
print("list2:",list2)
# list1中的第一个列表数据增加一个元素3
list1[0].append(3)
print("list1:",list1)
print("list2:",list2)
# list1中的元组数据拼接(50, 60)
list1[1] += (50, 60)
print("list1:",list1)
print("list2:",list2)
运行结果:
[1]
list1: [[1, 2], (30, 40), 100]
list2: [[1, 2], (30, 40)]
[2]
list1: [[1, 2, 3], (30, 40), 100]
list2: [[1, 2, 3], (30, 40)]
[3]
list1: [[1, 2, 3], (30, 40, 50, 60), 100]
list2: [[1, 2, 3], (30, 40)]

运行结果
[1] 给list1添加了一个元素,list2不受影响。因为浅拷贝之后list1和list2是两个不同的整体,地址不同。所以操作过后 list2 不变,list1 会发生改变。
[2] 对 list1 中的第一个列表新增元素 3。因为 list2 是 list1 的浅拷贝,list2 中的第一个元素和 list1 中的第一个元素,共同指向同一个列表,因此 list2 中的第一个列表也会相对应的新增元素 3。
[3] 因为元组是不可变的,这里表示对 list1 中的第二个元组拼接,然后重新创建了一个新元组作为 list1 中的第二个元素,而 list2 中没有引用新元组,因此 list2 并不受影响。

import copy

# 定义源对象
a = [1, 2, 3, 4, ['a', 'b']]
# 赋值,传递对象的引用
b = a
# 对象浅拷贝
c = copy.copy(a)
# 对象深拷贝
d = copy.deepcopy(a)
# 修改源对象
a.append(5)
# 修改源对象a中的['a', 'b']
a[4].append('c')
# 输出结果验证
print('a = ', a)
print('b = ', b)
print('c = ', c)
print('d = ', d)
运行结果:
a = [1, 2, 3, 4, ['a', 'b', 'c'], 5] # a新增一个元素5,且第五个元素增加一个元素'c'
b = [1, 2, 3, 4, ['a', 'b', 'c'], 5] # b是由a赋值,直接传递的,因此地址共享,a怎么变化,b就怎么变化
c = [1, 2, 3, 4, ['a', 'b', 'c']] # c是a的浅拷贝,因此a新增一个元素5,c不会发生变化,但是a的第四个元素新增一个元素'c',因为引用自一个实体,所以会发生改变
d =  [1, 2, 3, 4, ['a', 'b']] # d是a的深拷贝,无论a怎么变化,d都不会变化

希望大家看过之后能有所收获,有疑惑和收获欢迎评论区交流,写稿不易,记得点赞哦~~~

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值