python的浅拷贝和深拷贝

首先有以下几个概念:

变量:一个元素,具有指向对象的连接空间,也可以理解为是对象的引用;

对象:有分配的一块内存空间,用来存储其代表的值;

引用:一个从变量到对象的指针;

可变对象:可以改变的对象,可以添加、删除、修改对象内部元素的,比如像 列表、字典等;

不可变对象:在python中对象一旦创建,不可再对其内部元素进行修改的对象,比如像 元组、字符串

赋值、浅拷贝:

对于不可变对象赋值时

复制代码
a = '1'
b = a
print(a)
print(b)
print(id(a))
print(id(b))
a = '2'
print(a)
print(b)
print(id(a))
print(id(b))
b = '3'
print(a)
print(b)
print(id(a))
print(id(b))
复制代码
复制代码

1
1
31518400
31518400
2
1
31573408
31518400
2
3
31573408
30416576

复制代码

当a变更赋值对象时,b不会跟着变更

即初始时,a,b被分配了不同一引用同一地址

 

 

 

 当a重新赋值为2时,a的指向发生了变化,但是b的指向还是原来的

 

当b赋值为3时,b的指向发生了改变,但是a的指向未改变

 

对于可变对象赋值

复制代码
import copy
a = [1, 2, [4, 5, 6]]
b = a
c = copy.copy(a)
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))
a[0] = 7
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))
a[2][0] = 9
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))
c[2][0] = 10
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))
复制代码
复制代码

[1, 2, [4, 5, 6]]
[1, 2, [4, 5, 6]]
[1, 2, [4, 5, 6]]
23299144
23299144
23299240
[7, 2, [4, 5, 6]]
[7, 2, [4, 5, 6]]
[1, 2, [4, 5, 6]]
23299144
23299144
23299240
[7, 2, [9, 5, 6]]
[7, 2, [9, 5, 6]]
[1, 2, [9, 5, 6]]
23299144
23299144
23299240
[7, 2, [10, 5, 6]]
[7, 2, [10, 5, 6]]
[1, 2, [10, 5, 6]]
23299144
23299144
23299240

复制代码

可以看到,直接赋值时,a,c会同步变化,而使用浅拷贝的b有时不会随着a、c变化

初始a,b指向同一地址,c指向另外一个地址

 

 当a[0]变化时,a[0]和b[0]的指向改变,c[0]不变

 

当里面的子列表发生改变的时候,即a[2][0]改变,a[2][0],b[2][0],c[2][0]引用的地址全部改变

 

当c里面的子列表发生改变时,即c[2][0]改变,a,b,c同时发生改变

 

 

 所以对于可变对象,赋值的引用不会改变,而浅拷贝只拷贝对象的第一层,即第一层的引用会变,而其子对象的引用不会改变。

深拷贝

对不可变对象深拷贝

复制代码
import copy
a = 1
b = copy.deepcopy(a)
c = copy.copy(a)
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))
a = 2
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))
复制代码
复制代码
1
1
1
1584631964
1584631964
1584631964
2
1
1
1584631980
1584631964
1584631964
复制代码

可以看到对于不可变对象,赋值、浅拷贝、深拷贝都不会给新对象开辟内存,而只是拷贝对象的引用

对于可变对象的深拷贝

复制代码
import copy
a = [1, 2, [4, 5, 6]]
c = copy.deepcopy(a)
print(a)
print(c)
print(id(a))
print(id(c))
a[0] = 7
print(a)
print(c)
print(id(a))
print(id(c))
a[2][0] = 9
print(a)
print(c)
print(id(a))
print(id(c))
c[2][0] = 10
print(a)
print(c)
print(id(a))
print(id(c))
复制代码
复制代码
[1, 2, [4, 5, 6]]
[1, 2, [4, 5, 6]]
25546632
25546568
[7, 2, [4, 5, 6]]
[1, 2, [4, 5, 6]]
25546632
25546568
[7, 2, [9, 5, 6]]
[1, 2, [4, 5, 6]]
25546632
25546568
[7, 2, [9, 5, 6]]
[1, 2, [10, 5, 6]]
25546632
25546568
复制代码

初始时a指向一个地址,c指向另一个地址

 当a[0]发生变化时,a[0]指向发生改变,c[0]不变

 当a的子对象发生改变时,即a[2][0]改变,a[2][0]指向发生改变,c[2][0]不变

 

 当c的子对象发生改变时,即c[2][0]改变,a[2][0]指向不变,c[2][0]改变

 

 可以看到,对于可变对象的深拷贝会拷贝其所有的对象

对于python中的切片操作(c = a[:])其实是属于浅拷贝的范围,可以自行将浅拷贝的代码(c = copy.copy(a))换成切片操作,最后输出的结果是一样的。

 总结

对于没有子对象的操作,无论是可变对象还是不可变对象,初始对象改变后,浅拷贝和深拷贝的结果都不受影响;

对于有子对象的操作,初始对象改变后,不可变对象浅拷贝和深拷贝的结果都不受影响,可变对象浅拷贝会随着子对象的改变而发生改变,深拷贝不受影响。

所以在某些时候不想改变原始对象,尤其对象中包含子对象时,需要用到深拷贝。

而不关心原始对象的数据时,可以用浅拷贝,节约内存。

posted @   *小白*  阅读(112)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示