python 深复制和浅复制详解

时间:2022-07-23
本文章向大家介绍python 深复制和浅复制详解,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

id(x)函数

  • id()函数可以查看一个变量在内存中的地址

变量赋值给变量-拷贝引用

对于以下代码

>>> import copy
>>> a=[1,2,3]
>>> b=a
>>> id(a)
"""
4382960392
"""
>>> id(b)
"""
4382960392
"""
>>> id(a)==id(b)    #赋值后,两者的id相同,为true。
True
>>> b[0]=222222  #此时,改变b的第一个值,也会导致a值改变。
>>> print(a,b)
[222222, 2, 3] [222222, 2, 3] #a,b值同时改变
  • 此时会发现,对于b=a这条命令而言,b是a的拷贝,指向的是内存中同一块地址即id(a)==id(b)。 通过改变b的元素也可以起到改变a中元素的作用,同理,改变a中的元素也会改变b中的元素。

可变对象与不可变对象

  • 可变对象是指,一个对象在不改变其所指向的地址的前提下,可以修改其所指向的地址中的值; 值和地址不对应(列表)
  • 不可变对象是指,一个对象所指向的地址上值是不能修改的,如果你修改了这个对象的值,那么它指向的地址就改变了 值和地址相互对应(int,float,complex,long,str,unicode,tuple) , 元组 tuple就属于不可变对象

基本可变对象的深复制和浅复制

  • 基本可变对象不仅仅只有列表,还有字典等,这里使用列表举个简单的例子。
a_list = [1, 2, 3]
a_shallow_list = copy.copy(a_list)
a_deep_list = copy.deepcopy(a_list)
print("id of a_list", id(a_list), "id of a_shallow_list", id(a_shallow_list), "a_deep_list", id(a_deep_list))
# id of a_list 2249250705672 id of a_shallow_list 2249201900552 a_deep_list 2249201900424
  • 其均会指向一个新的地址,即a_list,a_shallow_list,a_deep_list相互之间不影响。
  • 但是其中的元素是int类型,是不可变对象,因此其只要其中数值不变地址也不会变化。
print("id of a_list[0]", id(a_list[0]), "id of a_shallow_list[0]", id(a_shallow_list[0]), "a_deep_list[0]", id(a_deep_list[0]))
# id of a_list[0] 1887096560 id of a_shallow_list[0] 1887096560 a_deep_list[0] 1887096560
# 基本可变对象中不可变对象的地址不会改变

基本不可变对象的深复制和浅复制

  • 元组 tuple是不可变对象,只要地址改变其中的值也会改变,因此 深复制和浅复制 都不会改变其中元素的地址。
a_tuple = (1, 2, 3)
a_shallow_tuple = copy.copy(a_tuple)
a_deep_tuple = copy.deepcopy(a_tuple)
# 比较基本不可变对象,深复制和浅复制区别
print("id of a_tuple", id(a_tuple), "a_shallow_tuple", id(a_shallow_tuple), "a_deep_tuple", id(a_deep_tuple))
print("id of a_tuple[0]", id(a_tuple[0]), "a_shallow_tuple[0]", id(a_shallow_tuple[0]), "a_deep_tuple[0]",
      id(a_deep_tuple[0]))
# id of a_tuple 2344837083280 a_shallow_tuple 2344837083280 a_deep_tuple 2344837083280
# id of a_tuple[0] 1885130480 a_shallow_tuple[0] 1885130480 a_deep_tuple[0] 1885130480

嵌套不可变对象的深复制和浅复制

  • 但是对于嵌套对象,只要其中包含 可以引用的可变对象 ,深复制就会重新分配内存创建新的对象 。由于外层是元组对象,是不可变对象,浅复制则不会重新分配内存。 这里是 深复制和浅复制的区别之一。 而这里浅复制不改变地址的操作,也就表示操纵浅复制的对象也可以对原始对象进行操作。
a1_tuple = (1, 2, (1, 2, 3), [1, 2, 3])
a1_shallow_tuple = copy.copy(a1_tuple)
a1_deep_tuple = copy.deepcopy(a1_tuple)

# 复合嵌套不可变元素的深复制和浅复制区别
print("id of a1_tuple", id(a1_tuple), "a1_shallow_tuple", id(a1_shallow_tuple), "a1_deep_tuple", id(a1_deep_tuple))
print("id of a1_tuple[3]", id(a1_tuple[3]), "a1_shallow_tuple[3]", id(a1_shallow_tuple[3]), "a1_deep_tuple[3]",
      id(a1_deep_tuple[3]))

# id of a1_tuple 2498218636296 a1_shallow_tuple 2498218636296 a1_deep_tuple 2498218638776
# id of a1_tuple[3] 2498267415048 a1_shallow_tuple[3] 2498267415048 a1_deep_tuple[3] 2498218716040

嵌套可变对象的深复制和浅复制

  • 这是常见的一种模式,也是网上博文最多讨论的一种模式,即外层是一个可变对象例如list,内层中也包含有可变对象list。
  • 浅复制会对外层可变对象进行复制,但是对内层可变对象不会复制,也就是说内层可变对象的地址不会改变。 这时改变拷贝对象的内层元素,原有对象内层对象也会改变。
  • 但是深复制不会有这种担忧,里里外外都是重新从内存中申请的空间,都是一个全新的对象。
  • 可以认为对于嵌套类型结构,深复制都是重新分配空间,建立的原来完全不同的对象。
  • 建议平时用深复制,不易出错~!
a1_list = [1, 2, (1, 2, 3), [1, 2, 3]]
a1_shallow_list = copy.copy(a1_list)
a1_deep_list = copy.deepcopy(a1_list)
# 复合嵌套可变元素的深复制和浅复制区别
print("id of a1_list", id(a1_list), "id of a1_shallow_list", id(a1_shallow_list), "a1_deep_list", id(a1_deep_list))
print("id of a1_list[3]", id(a1_list[3]), "id of a1_shallow_list[3]", id(a1_shallow_list[3]), "a1_deep_list[3]",
      id(a1_deep_list[3]))
# id of a1_list 1453555407752 id of a1_shallow_list 1453555447432 a1_deep_list 1453555477384
# id of a1_list[3] 1453604277640 id of a1_shallow_list[3] 1453604277640 a1_deep_list[3] 1453555448968