Python 浅复制与深复制(Shallow Copy & Deep Copy)

本文介绍了Python中赋值语句的引用机制,区分了浅复制和深复制的区别,以及Python对不可变对象的特殊处理。通过示例说明了如何避免浅复制带来的对象间影响,以及在类设计中的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Python中,赋值语句的意义是在对象与变量之间建立引用关系,相当于给对象起了名字。对于复合类型对象,由于其可以保存其他类型对象的引用,而在复制时即引出了“仅复制引用关系”或“同时复制引用对象”两种选择,即浅复制和深复制。

一、浅复制示例

Python中的赋值语句是建立对象与变量名的引用关系,下面语句即是将list1与列表对象建立引用关系:

list1 = ['abc']

在这里插入图片描述
而对于集合类型的对象,其中元素保存的可能又是到其他对象的引用,下面语句中,list1保存了3个对象,其中列表和元组是引用,指向其实际对象:

list1 = ['abc', [1,2], (3,4)]

在这里插入图片描述
对于list1,当对其进行复制时,就有了浅复制和深复制两种选择。默认的构造函数或切片采用的都是浅复制。下面代码中,list2是list1的浅复制副本(副本共享内部对象的引用):

list1 = ['abc', [1,2], (3,4)]
list2 = list1[:] 或list2 = list(list1)

在这里插入图片描述
改变list2中列表的元素,list1中的列表也改变了,可以证明它们引用的是相同的对象:

list2[1].append(3)
list1[1]

在这里插入图片描述
对象引用关系如下:
在这里插入图片描述

二、深复制示例

有时我们需要对象的深复制,即集合内部对象不共享引用。利用copy模块的deepcopy()函数可以为任意对象做深复制(其还有个copy()函数可以做任意对象的浅复制)。

下面是list1的深复制引用关系图,从结果来看,list1和list2中的列表已经独立复制了,为什么元组还是指向同一个对象?这其实是python内部的一个优化机制:

import copy
list1 = ['abc', [1,2], (3,4)]
list2 = copy.deepcopy(list1)

在这里插入图片描述
我们先测试一下变更list2中的列表和元组对象,可以发现list1和list2已经互不影响了:

list2[1].append(3)
list2[2] += (5,6)

在这里插入图片描述

2.1 python对不可变对象的优化

上面的例子中,我们对list2[2]的元组对象做+=操作,这个应该做原地变更,但由于元组是不可变对象,所以python会重新创建一个元组,然后重新绑定至list2[2]的引用上,从结果上来看,深复制对于不可以用对象依然复制引用关系并不会造成复制对象间的相互影响。
在这里插入图片描述
在深复制时,我们期望的是python会复制所有引用对象,而不仅仅是引用关系,但它会欺骗你。对于不可变对象python依然会复制引用关系,这其实是一种优化,防止重复创建对象。这在普通的常量上也可以体现,下面示例创建了2个变量a,b都等于1,但实际上python只创建了1个数字对象,然后让a,b都指向它(通过id()函数和is可以确定a,b指向的是同一个对象):

a = 1
b = 1
id(a), id(b)
a is b

在这里插入图片描述

可以总结出:浅复制可能会造成复制对象间的相互影响,有时你可以利用这种影响传递变化,但有时也可能造成意想不到的影响,特别是在类编写中,尽量避免用可变对象作为初始化参数,其可能造成实例之间的互相干扰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值