python易错盲点排查之+=与+的区别分析以及一些赋值运算踩过的坑详解编程语言

问题1. int和list是不一样的

 

>>> a=1 
>>> b=a 
>>> a+=1 
>>> a,b 
(2, 1) 
>>> a=[1,2,3,4] 
>>> b=a 
>>> a+=[5] 
>>> a,b 
([1, 2, 3, 4, 5], [1, 2, 3, 4, 5])

 

通俗地讲,类型为int时,a和b是“不一样的”;类型为list时,a和b是“一样的”。术语叫做immutable和mutable,具体原理在这个节点不必深究。
问题1.1. 我们通常运行b=a这一语句时,会直觉地认为,b和a已经不一样了。

>>> a=[[1],[2],[3],[4]] 
>>> b+=a[0:2] 
>>> b 
[1, 2, 3, 4, [1], [2]] 
>>> a=[[1],[2],[3],[4]] 
>>> b=[] 
>>> b+=a[0:2] 
>>> a,b 
([[1], [2], [3], [4]], [[1], [2]]) 
>>> b[0] 
[1] 
>>> b[0][0]='changed!' 
>>> # You don't expect a to change 
>>> # However 
>>> a, b 
([['changed!'], [2], [3], [4]], [['changed!'], [2]])

可以看到,a[0]的[1]和b[0]的[1]是“一样的”,因为改变b[0]就会改变a[0](注意不是改变b,是改变b[0]。改变b不会对a有任何影响)
问题2. list的情况下,a+=b和a=a+b是不一样的

>>> a=[1,2,3,4] 
>>> b=a 
>>> a+=[5] 
>>> a,b 
([1, 2, 3, 4, 5], [1, 2, 3, 4, 5]) 
>>> a=[1,2,3,4] 
>>> b=a 
>>> a=a+[5] 
>>> a,b 
([1, 2, 3, 4, 5], [1, 2, 3, 4])

同样通俗地讲,在+=的情况下,a还是原来的a,和b“一样”;在+的情况下,a已经不是原来的a了,和b“不一样”。
问题3. 如果要让+=和+行为一致,应该怎么做?

>>> import copy 
>>> a=[1,2,3,4] 
>>> b=copy.deepcopy(a) 
>>> a+=[5] 
>>> a,b 
([1, 2, 3, 4, 5], [1, 2, 3, 4])

这与问题2中a=a+b的情况结果一致了。当对list进行b=a时,实际上进行的是“引用”操作;只有使用b=copy.deepcopy(a)才是进行我们通常期望的“拷贝”操作。
问题4. 回到问题中的代码,当k=1时,以下代码:

subset += (elements[0:size])

根据问题1.1,subset与elements是“一样的”,因此未来改变subset的元素的操作有可能改变elements的元素

到这行代码时,注意set就是递归传递过来的subset:

#set[j] +=  (elements[i])  #Why Elements change here? 
set[j]  = set[j] +  (elements[i]) 

根据问题2,+=中set[j]依然是原来的set[j],也就可能是elements的元素。因此

set[j] += elements[i]

可能会等价于

elements[*] += elements[i]

一旦改变了elements的元素,结果自然就不对了。
怎么解决这个问题?根据问题3,只要保证set与elements是“不一样的”,就符合程序的逻辑。因此将

subset += (elements[0:size])

改为(记得import copy)

subset += copy.deepcopy(elements[0:size])

就能在+=的情况下正常运行了。
总结:在python中,list类型的赋值b=a进行的引用操作,而非拷贝操作,在需要拷贝操作时,需要加上b=copy.deepcopy(a)。(copy.copy和copy.deepcopy的区别超出问题范畴,有兴趣可以google)

 

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/11957.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论