问题发现
在字典/列表等对象里套用字典/对象,并且复制的时候会出现这个情况:
a={
'a1':{'line':'l1','param':[0,1,2]},
'a2':{'line':'l2','param':2}
}
print(a)
b=a['a1'].copy()
b['param'][0]=10
print('===a=====')
print(a)
print('===b=====')
print(b)
执行结果是:
===a before=====
{'a1': {'line': 'l1', 'param': [0, 1, 2]}, 'a2': {'line': 'l2', 'param': 2}}
===a after=====
{'a1': {'line': 'l1', 'param': [10, 1, 2]}, 'a2': {'line': 'l2', 'param': 2}}
===b after=====
{'line': 'l1', 'param': [10, 1, 2]}
可以发现,为了复制字典a中的字典a1给b,虽然b复制过去了,但是修改b的数值时会将a的值也一并修改。
问题分析
原因和python的复制方式有关,python一共分三种复制
直接赋值
直接赋值就是简单的=
,由于python中数字都有固定id,比如语句c = 1
意思是将1的id地址放到c的内容中。
而对于列表来说,c = [1,2,3]
的意思就是创建了一个新地址ADDR,里面存储[1,2,3],这个地址赋给了c的内容。所以当再加上一句d = c
之后,对d的操作同时也就是对c操作,这时候ADDR也同时赋给了d,所以当修改ADDR里面的内容时,c、d同时变化。
浅复制
和下面的深复制一看就知道区别。浅复制一般是使用自带的函数copy()
,如:
c = [1,2,3]
d = c.copy()
那么这个时候,d获得了[1,2,3]所存放的地址ADDR里的值,放到了ADDR2里,此时c和d没有任何关系,完全是两个陌路人。
既然叫做浅复制,那么肯定的,这个复制只覆盖了一层,也就是说,假如c中存储的ADDR,其内容不是[1,2,3]
,而是另一个列表e的地址,那只能复制到ADDR这一层:
e = [1,2,3]
c = [e,4,5]
d = c.copy()
print(c)
d[0][0]=10
d[1]=40
print(c)
print(d)
执行结果是:
[[1, 2, 3], 4, 5]
[[10, 2, 3], 4, 5]
[[10, 2, 3], 40, 5]
明显的,c中存储的第一个数据是e的地址,所以即便拷给了d,仍然是指向e,c和d共享e的变化,然而后面的4,5就互相没有关系。
浅复制有几种方式:
循环赋值,采用遍历的方式将复制对象append添加到新列表中
最简单的赋值,比如
d[0] = c[1]
来指定复制切片,使用类似
d = c[:]
的语句来复制整个对象
当然上面的方式都只复制第一层
深复制
深复制即完全复制所有内容,复制之后的对象和原来没有任何关系。使用时需要import模块copy,使用deepcopy()复制。
解决方案
有多层嵌套的时候,用deepcopy()是最方便的做法。对于特定需求下,也可以采用浅复制的办法,但是记住只复制一层,要继续叠加层次时就要考虑还用不用这个弱鸡的copy()。
#solution 1
from copy import deepcopy
a={
'a1':{'line':'l1','param':[0,1,2]},
'a2':{'line':'l2','param':2}
}
print('===a before=====')
print(a)
b=deepcopy(a['a1'])
b['param'][0]=10
print('===a after=====')
print(a)
print('===b after=====')
print(b)
#solution 2
from copy import deepcopy
a={
'a1':{'line':'l1','param':[0,1,2]},
'a2':{'line':'l2','param':2}
}
print('===a before=====')
print(a)
b=deepcopy(a['a1'])
b['param'][0]=10
print('===a after=====')
print(a)
print('===b after=====')
print(b)
运行结果如下,其实有时候复制a,需要的其实只是a的一小部分,全部复制有时候是挺浪费资源的。
#solution 1:
===a before=====
{'a1': {'line': 'l1', 'param': [0, 1, 2]}, 'a2': {'line': 'l2', 'param': 2}}
===a after=====
{'a1': {'line': 'l1', 'param': [0, 1, 2]}, 'a2': {'line': 'l2', 'param': 2}}
===b after=====
{'line': 'l1', 'param': [10, 1, 2]}
#solution 2:
===a before=====
{'a1': {'line': 'l1', 'param': [0, 1, 2]}, 'a2': {'line': 'l2', 'param': 2}}
===a after=====
{'a1': {'line': 'l1', 'param': [0, 1, 2]}, 'a2': {'line': 'l2', 'param': 2}}
===b after=====
[10, 1, 2]
Problem solved.