說到python的增量賦值,大家里面就想到 +=, *= 之類的
+=背后的特殊方法是 iadd 意思是:就地加法,如果一個類沒有實現(xiàn)這個方法,那么python會退一步使用 add來進行相加
a += b
如果a實現(xiàn)了就地相加方法,就是調(diào)用這個方法,同時對可變序列來說,就是直接改動,就像調(diào)用a.extend(b)一樣,但是如果a沒有這個方法,那就執(zhí)行 a = a + b, 總的來說,可變序列一般都實現(xiàn)了就低價法,而不變序列根部就不支持這個操作,也不可能實現(xiàn)這個方法。
現(xiàn)在我們得出的結(jié)論是:
可變序列可以增量賦值,不可變序列不可以增量賦值
這個結(jié)論到底對不對?我們來看一個題:
t = (1,2,[3,4])
t[2] += [5,6]
用我們剛才的結(jié)論來判斷,因為t是個tuple,是不變序列,所以不支持增量賦值,所以這個肯定會報錯,沒錯,沒錯,我們用剛才的結(jié)論成功的做出了這題,來看報錯信息:
<ipython-input-43-c823147bfbc0> in <module>()
----> 1 t[2] += [5,6]
TypeError: 'tuple' object does not support item assignment
哈哈,很簡單,接著,我們再來看一下此時t的值:
In [44]: t
Out[44]: (1, 2, [3, 4, 5, 6])
你肯定會打呼:what the f**k!!!!,什么鬼,怎么還是變了,擦
結(jié)果: t被改動了,同時也拋出了錯誤
來吧,我們來看看這其中的原理
首先我們看看python字節(jié)碼對于s[a] += b這種類型的解析
In [46]: import dis
In [47]: dis.dis('s[a] += b')
1 0 LOAD_NAME 0 (s)
3 LOAD_NAME 1 (a)
6 DUP_TOP_TWO
7 BINARY_SUBSCR ------1
8 LOAD_NAME 2 (b)
11 INPLACE_ADD ---------2
12 ROT_THREE
13 STORE_SUBSCR ------------3
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
三個步驟(對應代碼里面的1,2,3):
1.將s[a]存入棧頂
2.完成s[a] += b
3.存貯結(jié)果
有結(jié)果可以看出前兩個步驟成功執(zhí)行了,在最后一步報錯,因為s是不可變的序列所以s[a] 賦值失敗報錯,通過這個例子我們可以得到的結(jié)論如下:
1.盡量不要把可變對象放到元組里面(通過extend方法可以避免這個問題)
2.增量賦值 操作不是原子操作,因為報錯了,但是還是完成了 += 操作
這個問題是個不常遇見的邊界問題,不常見,但是了解一下還是很有用處的~