python的深浅拷贝

学习深浅拷贝,需要先了解一下列表在内存中的存储方式

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地址,一个是新创建一个地址.

一个不完全隔离, 一个完全隔离.

posted @ 2020-08-18 18:08  Mn猿  阅读(107)  评论(0编辑  收藏  举报