实时焦点:浅显直白的Python深拷贝与浅拷贝区别说明

一、可变数据类型与不可变数据类型

在开始说深拷贝与浅拷贝前,我们先来弄清楚,可变对象与不可变对象

总的来说,Python数据类型可分为可变数据类型与不可变数据类型


(资料图)

可变数据类型:在不改变对象所指向的地址的前提下,地址中的值是可以改变的,例如列表[1, 2, 3],我们可以改为[2,3]并不需要变更它指向的地址。列表、字典、集合都是可变数据类型

不可变数据类型:在不改变对象所指向的地址的前提下,地址中的值是不可变的,所以如果修改了对象的值,就相当于在另一个新的地址,存储了新的值。Python中元组、字符串、数值、布尔值都是不可变数据类型。

二、Python深拷贝与浅拷贝的区别

在弄清楚了可变对象和不可变对象之后,我们进入正题,看下Python的深拷贝与浅拷贝的区别

1. 浅拷贝:

仅拷贝父对象,可理解为仅拷贝对象第一层。浅拷贝之后,新旧对象本身指向的地址不同了,但子对象指向的地址仍然相同,我们可以用copy.copy()和可变数据类型的切片来进行浅拷贝

m = [1, 0, [2, 3, 4, [5, 6]]]n = m.copy()p = m[:]print(f'对象m的地址是{id(m)},对象n的地址是{id(n)},对象p的地址是{id(p)}')print(f'm[0]的地址是{id(m[0])},n[0]的地址是{id(n[0])},p[0]的地址是{id(p[0])}')print(f'm[2]的地址是{id(m[2])},n[2]的地址是{id(n[2])},p[2]的地址是{id(p[2])}')输出:

对象m的地址是1322908811144,对象n的地址是1322908811080,对象p的地址是1322908763400m[0]的地址是140727539432512,n[0]的地址是140727539432512,p[0]的地址是140727539432512m[2]的地址是1322908811208,n[2]的地址是1322908811208,p[2]的地址是1322908811208

打印结果可以看到,浅拷贝之后,新对象n, p的地址与m不同,但n, p的子对象地址与m中子对象地址是相同的

此时,我们对新对象的子对象进行修改,我们来修改一下n[0]看一下结果

n[0] =99print(f' m[0]={m[0]}\n n[0]={n[0]}\n p[0]={p[0]}')print(f' m[0]的地址:{id(m[0])}\n n[0]的地址:{id(n[0])}\n p[0]的地址:{id(p[0])}')输出: m[0]=1 n[0]=99 p[0]=1 m[0]的地址:140727543364672 n[0]的地址:140727543367808 p[0]的地址:140727543364672

可以看到,n[0]的地址和值都变了, m[0]和p[0]并没有变,是为什么呢?

记得咱们最开始介绍了可变对象和不可变对象,这里的n[0]是数值,是不可变对象,所以在地址不改变的情况下,它的值是不变的;

而我们在给它赋值时,相当于是把它指向了另一个地址,存储新值,而m[0]和p[0]指向的地址并没有变化

接下来咱们再来尝试变更一下n[2]吧

print('----------------------------修改前----------------------------------\n '      f'm[2]的地址:{id(m[2])}\n n[2]的地址:{id(n[2])}\n p[2]的地址:{id(p[2])}')n[2][1] = 'n21'print('----------------------------修改后----------------------------------\n'      f' m[2]={m[2]}\n n[2]={n[2]}\n p[2]={p[2]}')print(f' m[2]的地址:{id(m[2])}\n n[2]的地址:{id(n[2])}\n p[2]的地址:{id(p[2])}')输出:----------------------------修改前---------------------------------- m[2]的地址:2235118923976 n[2]的地址:2235118923976 p[2]的地址:2235118923976----------------------------修改后---------------------------------- m[2]=[2, 'n21', 4, [5, 6]] n[2]=[2, 'n21', 4, [5, 6]] p[2]=[2, 'n21', 4, [5, 6]] m[2]的地址:2235118923976 n[2]的地址:2235118923976 p[2]的地址:2235118923976

可以看到,变更前后n[2]、m[2]和p[2]的地址都是没变的, 原因你已经知道了吧,是的,因为n[2]是可变对象,是可以在地址中直接变更值的。

2. 深拷贝

深拷贝完全父对象与子对象。可使用copy模块的deepcopy()方法进行深拷贝,此外使用for循环复制可迭代序列也是深拷贝

m = [1, 0, [2, 3, 4, [5, 6]]]n1 = copy.deepcopy(m)print(f'对象m的地址是{id(m)},对象n1的地址是{id(n1)}')print(f'm[0]的地址是{id(m[0])},n1[0]的地址是{id(n1[0])}')print(f'm[2]的地址是{id(m[2])},n1[2]的地址是{id(n1[2])}')输出:对象m的地址是2551218893640,对象n1的地址是2551219173192m[0]的地址是140727516494912,n1[0]的地址是140727516494912m[2]的地址是2551218833480,n1[2]的地址是2551221308040

打印结果可以看出,新旧对象本身的地址,和可变子对象地址都是不同的。

这里看到m[0]和n1[0]的地址相同,但不可变对象的值变更地址就会变更,所以不会有问题。

三、 总结

综上,咱们在实际应用中,如果拷贝对象的子对象都是不可变对象,那么使用浅拷贝和深拷贝都行,

但如果待拷贝对象中有可变子对象,需要注意根据实际需求选择使用深拷贝还是浅拷贝。

关键词: 数据类型 可变数据

为您推荐

实时焦点:浅显直白的Python深拷贝与浅拷贝区别说明

一、可变数据类型与不可变数据类型在开始说深拷贝与浅拷贝前,我们先来弄清楚,可变对象与不可变对象总的来说,Python数据类型可分为可变数据

来源:哔哩哔哩2023-02-23

播放奇妙动画_微妙动画|世界视讯

1、微妙动画是一个大型的CG技术交流论坛。2、很大。3、地址是*** com。本文到此分享完毕,希望对大家有所帮助。

来源:元宇宙网2023-02-23

当前观察:预计12月上市 上汽大众ID.3将在成都车展亮相

现在汽车越来越普及,基本上家家户户很快都会有车。汽车这么多,我们在用车的过程中肯定会遇到各种各样的问题。那么这个时候,我

来源:互联网2023-02-22

世界热门:育儿假新规2023年什么时候执行?育儿假新规多少天2022-2023新规

一、2022年育儿假多少天?1、北京市北京市按法规生育子女的夫妻,在子女满三周岁前,每人每年享受五个工作日的育儿假;每年按照子女满周岁计算

来源:律法网2023-02-22

前沿热点:【病娇丨护夫】想占有我的少女是天外来物【07】

不知名的道路上,两辆汽车正极速飞驰着,车厢内,我被诸位长辈围在中间。所有人都没有说话,仿佛空气也凝固了一般,就在刚刚,我终于承认了我

来源:哔哩哔哩2023-02-22

辟谣工作室|“最小贪官”小学副班长受贿数万元?当地教育部门:网传不实 每日讯息

辟谣工作室|“最小贪官”小学副班长受贿数万元?当地教育部门:网传不实

来源:上游新闻2023-02-22

快报:国产新车报道:2020广州车展:AMG GLA35售43.98万元

你想了解最新最前沿的汽车资讯吗?你想了解国产神车的最新相关报道吗?对于买车的朋友们来说了解到一手汽车消息是非常有必要的,

来源:互联网2023-02-22

全球观点:9026米!陕西钻井装备打出亚洲最深直井

本报讯(记者沈谦)2月20日,记者从省工信厅获悉:宝鸡石油机械有限责任公司(以下简称宝石机械)研制的9000米自动化钻机,于日前助力西南油气

来源:陕西日报2023-02-22