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