python的浅拷贝和深拷贝

1. Python如何拷贝一个对象?

在Python中如何拷贝一个对象呢?我们很多时候会用等号赋值法,除了=赋值,实际上还有浅拷贝和深拷贝,那么赋值,浅拷贝和深拷贝到底有什么区别呢?

(1) 赋值(=):创建了对象的一个新的引用,修改其中任意一个变量都会影响到另外一个;

(2) 浅拷贝:创建一个新的对象,但是它包含的是对原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另外一个也会改变),即浅拷贝只拷贝顶层引用;

(3) 深拷贝:创建一个新对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变),即深拷贝会逐层进行拷贝,直到拷贝的所有引用都是不可变引用为止。

浅拷贝和深拷贝从概念上看有点抽象,我们可以通过下面的例子来说明:

2. 举例说明

例如有列表 l1=['aaa', 66, [1, 2, 3]],l2是对l1的赋值,l3是对l1的浅拷贝,l4是对l1的深拷贝。

>>> l1 = ['aaa', 66, [1, 2, 3]]
>>> l2 = l1
>>> l3 = l1.copy()
>>> l4 = copy.deepcopy(l1)
>>> id(l1)
140214926880712     ------> 指向同一块内存地址
>>> id(l2)
140214926880712     ------> 赋值是拷贝引用,指向同一块内存地址
>>> id(l3)
140215054238536     ------> 创建新的对象,即分配新的内存地址
>>> id(l4)
140214924504200

再打印列表l1的第3个元素的id:
>>> id(l1[2])
140214926939592     
>>> id(l2[2])
140214926939592     ------> 赋值后第三个元素指向[1, 2, 3]的地址 可变对象
>>> id(l3[2])
140214926939592     ------> 浅拷贝后第三个元素指向[1, 2, 3]的地址 可变对象,这就是所谓的浅拷贝,只拷贝顶层引用
>>> id(l4[2])
140214926938440     ------> 深拷贝赋值所有的内容,所以第三个元素的内存地址与赋值和浅拷贝均不同,这就是所谓的深拷贝,递归赋值

修改l1的第3个元素,查看赋值,浅拷贝,深拷贝的列表值的变化
>>> l1[2][0] = 4
>>> l1
['aaa', 66, [4, 2, 3]]  
>>> l2
['aaa', 66, [4, 2, 3]]   ------> 因为赋值l2是拷贝的l1的引用,l1的元素变化l2会跟着变化
>>> l3
['aaa', 66, [4, 2, 3]]   ------> 因为浅拷贝l3的第三个元素拷贝的引用,故l3的第三个元素也会变化
>>> l4
['aaa', 66, [1, 2, 3]]   ------> 可以看出深拷贝并不会因为l1的变化而影响,因为深拷贝会逐层进行拷贝,直到拷贝的所有引用都是不可变引用为止

接下来在列表l1追加一个元素,观察赋值,浅拷贝,深拷贝的列表值的变化:
>>> l1.append(88)
>>> l1
['aaa', 66, [4, 2, 3], 88]
>>> l2
['aaa', 66, [4, 2, 3], 88]  -------> 同理因为赋值l2是拷贝的l1的引用,l1的元素变化l2会跟着变化
>>> l3
['aaa', 66, [4, 2, 3]]      -------> 到这里应该就容易理解了吧,因为浅拷贝l3的内存是新分配的,故l1追加元素并不会影响浅拷贝l3的值
>>> l4
['aaa', 66, [1, 2, 3]]      -------> 和我们预期的一样,深拷贝依然不受影响

在这里补充下例子中的浅拷贝,在Python中一切皆是对象,通过概念我们可以看出浅拷贝是创建了一个新的对象,但是这个新对象中的内容仍然是指向原始对象中包含项的引用,可以理解为浅拷贝是新对象,但是对象的包含项仍然是旧的引用。所以上面例子中,l1[2][0] =4 修改列表l1后浅拷贝的第3个元素会随着l1的改变而变化,因为[1,2,3]本身就是个可变的列表,其在初始化时内存地址已经分配好,这个把内存地址拷贝给了l3;而 l1.aapend(88)运行后l3列表并没有追加该元素,因为l3是个对象,是个新对象,与l1的内存地址不同,所以不会受影响。不知道我这里有没有讲清楚。

 

3. 总结:

赋值:默认浅拷贝传递对象的引用而已,原始列表改变,被赋值的b也会做相同的改变;

浅拷贝:没有拷贝子对象,所以原始数据改变,子对象会改变

深拷贝:包含对象里面的子对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变

在其他帖子看到对浅拷贝和深拷贝这样的总结,挺有意思的:

浅拷贝就是藕断丝连    ----只拷贝顶层引用,子项还存在引用关系
深拷贝就是离婚了 ----彻底没关系了

 

为什么Python默认的拷贝方式是浅拷贝?

时间角度:浅拷贝花费时间更少;

空间角度:浅拷贝花费内存更少;

效率角度:浅拷贝只拷贝顶层数据,一般情况下比深拷贝效率高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值