之前写马氏链的算法时,对于一个字典套列表(例如a={"a":a,"b":[1,2,3]}),我直接使用b=a这种方式进行赋值(拷贝),储存a的同时,再使用a的值使用其他方法进行操作,并且作为b储存。但是实际情况是,a的值赋给b后,在b变化的同时(这里的b不是被重新用“=”赋值,如b["b"].append(4)这样变化),a的值也变化成了b现在的值(a == b == {"a":a,"b":[1,2,3,4]})。
这里就引入了深拷贝和浅拷贝的概念了:
对于我们常识中所以为的复制,就是将被复制对象完完全全复制一遍,由内而外作为一个新的对象而存在,被复制对象再做什么更改此时也和已经复制出的新对象没有关系了。
对于上述我们常识中的拷贝,实际上就是深拷贝。深拷贝是对于一个对象所有层次的拷贝(递归);
而浅拷贝是对于一个对象的顶层拷贝,其中浅拷贝又分为以下两种:
b=a #浅拷贝: 对象的引用(别名)。
b=copy.copy(a) #浅拷贝:拷贝父对象,不会拷贝对象的内部的子对象。
深拷贝是:
b=copy.deepcopy(a) #copy模块的deepcopy方法,完全拷贝了父对象及其子对象。
对于简单的对象,比如a ="123",从值来看,用赋值拷贝和copy方法中的浅拷贝,深拷贝看不出什么区别;但是对于列表,赋值拷贝与copy浅拷贝深拷贝的值就有了区别,对于字典嵌套列表,copy的浅拷贝和深拷贝又有了新的区别。
现在上代码实践:
import copy
a = "as"#字符串时
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
print(a, id(a))
print(b, id(b))
print(c, id(c))
print(d, id(d))
a="asf"
b
print(a, id(a))
print(b, id(b))
print(c, id(c))
print(d, id(d))
结果1:

此时,a变化时,bcd都没有变化,并且bcd的内存地址也没随a的地址变化而变化。
import copy
a = [1,2,3]#列表时(一层结构)
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
print(a, id(a))
print(b, id(b))
print(c, id(c))
print(d, id(d))
a.append(4)
b
print(a, id(a))
print(b, id(b))
print(c, id(c))
print(d, id(d))
结果2:

此时b的内存地址和a相同,cd内存地址和a不同(c,d两者也不同);a变化后,b的值随着a的变化发生了变化,物理地址还与a相同,而c和d值和物理地址在a变化前后都没有发生变化。
import copy
a = {"a":2,"b":[1,2]}#字典嵌套列表时(二层结构)
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
print(a, id(a))
print(b, id(b))
print(c, id(c))
print(d, id(d))
a["b"].append(3)
b
print(a, id(a))
print(b, id(b))
print(c, id(c))
print(d, id(d))
结果3:

此时b内存地址和a相同,cd内存地址和a不同。a变化后,bc的值随a变化而变化,d的值和内存地址都没有变化。
总结:
-
b = a: 赋值拷贝(浅拷贝),a 和 b 都指向同一个对象。
赋值拷贝 -
b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。
浅拷贝 -
b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。
深拷贝
图拷自:
http://www.runoob.com/w3cnote/python-understanding-dict-copy-shallow-or-deep.html


