深浅拷贝
学习深浅拷贝,需要先了解一下列表在内存中的存储方式
1. 列表在内存中存储方式
1.1 创建列表
lst = ['ymn', 'xcy', [9, 99]]
列表在内存中实际上存放的并不是一个,一个的值, 列表在内存中存放的是索引和对应值的内存地址
所以我们通过列表的索引取值, 实际上是通过索引取到对应的值的地址
2. 列表的引用
2.1 创建列表并引用
lst = ['ymn', 'xcy', [9, 99]]
lst1 = lst
print(id(lst)) # 1442941688264
print(id(lst1))# 1442941688264
这里在堆区并没有重新开辟空间存放新的值, 我们通过画图来看一下在内存中发生的事情.
lst 和 lst1 都是指向同一个地址, 如果lst1的值发生改变, lst也是随之改变的
3. 浅拷贝
浅拷贝: 可变和不可变元素都是直接拷贝地址, 两个列表可能会黏在一起.
3.1 创建列表并浅拷贝
lst = ['ymn', 'xcy', [9, 99]]
lst2 = lst.copy()
print(id(lst)) # 1442941688264
print(id(lst2))# 1758475685768
print(id(lst[0])) # 1309438439403
print(id(lst2[0])) # 1309438439403
print(id(lst[2])) # 1309438999999
print(id(lst2[2])) # 1309438999999
同样我们也是通过画图来观察浅拷贝会在内存中发生什么变化
从图中我们可以看出, 在内存中只是把之前原列表的每个元素的内存copy了一份放在了一个新的列表中, 你可以理解成换了瓶子(列表),并没有换酒(元素),还是指向原来的那些值。
3.2 修改原列表
3.2.1 修改不可变的元素
lst = ['ymn', 'xcy', [9, 99]]
lst2 = lst.copy()
lst[0] = 'YMN'
print(lst) # ['YMN', 'xcy', [9, 99]]
print(lst2) # ['ymn', 'xcy', [9, 99]]
当我们通过索引修改原列表的第一个值时,会在内存中开辟一个新的空间存放至,然后原列表会断掉之前索引和地址指向,并将索引指向新的地址 。又因为浅拷贝的列表中的地址是和原列表的一样,所以他的地址还是原来的地址并没有发生改变所以还是原来的值。
3.2.2 修改可变元素
lst = ['ymn', 'xcy', [9, 99]]
lst2 = lst.copy()
lst[2][1] = 999
print(lst) # ['ymn', 'xcy', [9, 999]]
print(lst2) # ['ymn', 'xcy', [9, 999]]
因为这次修改的是可变元素里面的值, 由于浅拷贝的列表指向的地址和原来一样,再加上是对元素的元素修改,索引浅拷贝的列表的索引指向的地址并没有发生改变,但是这个元素的内部发生了改变,所以原列表和浅拷贝的列表都会发生改变。
4. 深拷贝
深拷贝: 不可变元素还是直接拷贝地址, 不可变元素创建一个新的地址存放, 两个列表完全隔离.
4.1 创建列表并进行深拷贝
import copy
lst = ['ymn', 'xcy', [9, 99]]
lst3 = copy.deepcopy(lst)
print(id(lst)) # 1234292221819
print(id(lst3)) # 1234292676878
print(id(lst[1])) # 1442922218416
print(id(lst3[1])) # 1442922218416
print(id(lst[2])) # 2777233030152
print(id(lst3[2])) # 2777215797064
通过id我们也可以发现,深拷贝会把可变和不可变数据进行区分,不可变的直接把地址copy一份,而可变的则是
重新开辟空间,分配新的地址。
4.2 修改原列表
首先修改不可变的元素我们就不介绍了,这个在内存中发生的变化是和浅拷贝一样的。
4.2.1 修改可变元素
import copy
lst = ['ymn', 'xcy', [9, 99]]
lst3 = copy.deepcopy(lst)
lst[2][1] = 999
print(lst) # ['ymn', 'xcy', [9, 999]]
print(lst2) # ['ymn', 'xcy', [9, 99]]
因为这次可变元素是重新在内存中创建的,所以当原列表的可变元素的值发生变化,并不影响新的列表,新的列表的索引仍然对应着原来的地址,指向原来的值。
5. 总结
浅拷贝和深拷贝的本质区别就是对待可变元素,一个是直接copy地址,一个是新创建一个地址.
一个不完全隔离, 一个完全隔离.