01 深浅copy

Posted yang1333

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了01 深浅copy相关的知识,希望对你有一定的参考价值。

01 深浅copy

一、为什么要有深浅拷贝?

  • 当涉及到容器类型的修改操作时,想要保留原来的数据和修改后的数据,这个时候就需要深浅拷贝。

二、赋值操作

技术图片

source_list = ['aaaa', 1111, ['bbbb', 2222]]
new_list = source_list
# 观察1:源列表与新列表都是指向同一内存地址
print('-------------------------观察1---------------------------')
print(id(source_list), id(new_list))    # 2119311293696 2119311293696

# 修改不可变类型
source_list[0] = 'AAAA'
# 修改可变类型中的值
source_list[-1][-1] = 3333

# 观察2:只要有一个人的列表中的索引所对应的值的内存地址改变,则都改变
print('-------------------------观察2---------------------------')
print(id(source_list), id(new_list))    # 2119311293696 2119311293696
print(id(source_list[0]), id(new_list[0]))  # 2119312019888 2119312019888
print(id(source_list[-1][-1]), id(new_list[-1][-1]))    # 2119311395248 2119311395248

# 总结:
"""
赋值操作列表与新列表都是指向同一内存地址,2个列表中,只要有一个人的列表中的索引所对应的值的内存地址改变,则都改变
"""
  • 结论:赋值操作是把源列表容器的内存地址完完整整的多绑定一份交给新列表。

三、浅拷贝

  • 用法:list.copy()

  • 观察1:对源列表copy以后,产生的新列表内存地址发生了改变,不再是同一个列表。而新列表与源列表中的可变不可变类型的值在没修改之前都是指向同一个值。

技术图片

  • 观察2:对源列表中不可变类型的值进行修改以后,对于不可变类型的值,都是产生新值,让源列表的索引指向新的内存地址,并不会影响新列表。

技术图片

  • 观察3:对源列表中可变类型的值进行修改以后,对于可变类型,我们可以改变类型中包含的值,但这个可变容器本身内存地址不变。即新列表的索引仍然指向原来的内存地址,于是新列表也跟着受影响。

技术图片

  • 代码示例
source_list = ['egon', 'alex', [1, 2]]
new_list = source_list.copy()
# 观察1:对源列表copy以后,产生的新列表内存地址发生了改变,不再是同一个列表。而新列表与源列表中的可变不可变类型的值在没修改之前都是指向同一个值。
print('-------------------------观察1---------------------------')
print(new_list)     # ['egon', 'alex', [1, 2]]
print(id(source_list), id(new_list))
print(id(source_list[0]), id(source_list[1]), id(source_list[2]))
print(id(new_list[0]), id(new_list[1]), id(new_list[2]))

# 观察2:对源列表中不可变类型的值进行修改以后,对于不可变类型的值,都是产生新值,让源列表的索引指向新的内存地址,并不会影响新列表。
print('-------------------------观察2---------------------------')
source_list[0] = 'EGON'
source_list[1] = 'ALEX'
print(source_list)     # ['EGON', 'ALEX', [1, 2]]
print(id(source_list[0]), id(source_list[1]), id(source_list[2]))
print(id(new_list[0]), id(new_list[1]), id(new_list[2]))

# 观察3:对源列表中可变类型的值进行修改以后,对于可变类型,我们可以改变类型中包含的值,但这个可变容器本身内存地址不变。即新列表的索引仍然指向原来的内存地址,于是新列表也跟着受影响。
print('-------------------------观察3---------------------------')
print(id(source_list[2]), id(source_list[2][0]), id(source_list[2][1]))
print(id(new_list[2]), id(new_list[2][0]), id(new_list[2][1]))
source_list[-1][0] = 111
source_list[-1][1] = 222
print(new_list)
print(source_list)     # ['EGON', 'ALEX', [1, 2]]
print(id(source_list[2]), id(source_list[2][0]), id(source_list[2][1]))
print(id(new_list[2]), id(new_list[2][0]), id(new_list[2][1]))

# 总结
"""
1、对源列表copy以后,产生的新列表内存地址发生了改变,不再是同一个列表。而新列表与源列表中的可变不可变类型的值在没修改之前都是指向同一个值。
2、对源列表中不可变类型的值进行修改以后,对于不可变类型的值,都是产生新值,让源列表的索引指向新的内存地址,并不会影响新列表。
3、对源列表中可变类型的值进行修改以后,对于可变类型,我们可以改变类型中包含的值,但这个可变容器本身内存地址不变。即新列表的索引仍然指向原来的内存地址,于是新列表也跟着受影响。
"""
# 引申:综合观察2与观察3可以得出,想要copy得到新的列表与源列表的改操作完全独立开,必须有一种可以区分开可变类型与不可变类型的copy机制,这就是深copy。
  • 结论:把源列表第一层的内存地址不加区分的完全copy给一份新列表。(这里区分指的是可变,不可变类型的区分)

四、深拷贝

  • 深拷贝对容器类型中的每一层得数据加以区分,对可变不可变类型区分对待。
    • 争对不可变类型,拷贝以后任然还是用原来值的内存地址(省空间),但是当值一改,这时会开辟新的内存空间产生新的值,并与之绑定,于此同时两者之间互不影响。
    • 争对可变类型,拷贝以后会开辟新得容器地址,在这个新的容器地址中,再次区分可变不可变类型。如果是不可变类型任然用原来值得内存地址(省空间),但是当值一改,这时又会开辟新的内存空间产生新的值,并与之绑定。如果是可变类型则又会开辟新得容器地址,再次对容器中得可变不可变类型进行判断、区分,以此类推。
  • 用法:第一步:import copy 第二步:copy.deepcopy(list)
  • 观察1:

技术图片

  • 观察2:

技术图片

  • 观察3:

技术图片

  • 代码示例
import copy
source_list = ['egon', 'alex', [1, 2]]
new_list = copy.deepcopy(source_list)

print(new_list)
print(id(source_list), id(new_list))

print('------------查看容器第一层------------')
print('     不可变         不可变           可变')
print(id(source_list[0]), id(source_list[1]), id(source_list[2]))
print(id(new_list[0]), id(new_list[1]), id(new_list[2]))

print('------------查看容器第二层------------')
print(id(source_list[2][0]), id(source_list[2][1]))
print(id(new_list[2][0]), id(new_list[2][1]))

# 修改容器第一层的值:修改不可变类型
source_list[0] = 'EOGN'
source_list[1] = 'ALEX'

# 修改容器第二层的值:修改可变类型
source_list[2][0] = 111
source_list[2][1] = 222

print('------修改以后,修改源列表,新列表所有值不会一起受影响-------')
print(source_list)
print(new_list)
  • 结论:把两个列表完完整整独立开,并且只争对该操作而不是读操作。

深浅使用总结

  • 使用浅copy:如果你想对容器类型进行修改操作时,想保留原来的数据和修改后的数据,这时只有容器类型的第一层全是不可变类型,这个时候就用浅copy。
  • 使用深copy:如果你想对容器类型进行修改操作时,想保留原来的数据和修改后的数据,且你想让两个列表完完全全独立开,这个时候就用深copy。

以上是关于01 深浅copy的主要内容,如果未能解决你的问题,请参考以下文章

python集合深浅copy

python 06 id is == set 深浅copy

day01-关于深浅copy一些问题

集合及深浅copy 01

is == id 的用法;代码块;深浅copy;集合

小数据池 深浅copy 集合