python 序列增量赋值谜通常的题目

说到python的增量赋值,你们里面就想到 +=, *= 之类的python

+=背后的特殊方法是 iadd 意思是:就地加法,若是一个类没有实现这个方法,那么python会退一步使用 add来进行相加code

a += b

若是a实现了就地相加方法,就是调用这个方法,同时对可变序列来讲,就是直接改动,就像调用a.extend(b)同样,可是若是a没有这个方法,那就执行 a = a + b, 总的来讲,可变序列通常都实现了就低价法,而不变序列根部就不支持这个操做,也不可能实现这个方法。对象

如今咱们得出的结论是:
可变序列能够增量赋值,不可变序列不能够增量赋值
这个结论到底对不对?咱们来看一个题:ip

t = (1,2,[3,4])
t[2] += [5,6]

用咱们刚才的结论来判断,由于t是个tuple,是不变序列,因此不支持增量赋值,因此这个确定会报错,没错,没错,咱们用刚才的结论成功的作出了这题,来看报错信息:input

<ipython-input-43-c823147bfbc0> in <module>()
----> 1 t[2] += [5,6]

TypeError: 'tuple' object does not support item assignment

哈哈,很简单,接着,咱们再来看一下此时t的值:it

In [44]: t
Out[44]: (1, 2, [3, 4, 5, 6])

你确定会打呼:what the f**k!!!!,什么鬼,怎么仍是变了,擦
结果: t被改动了,同时也抛出了错误class

来吧,咱们来看看这其中的原理
首先咱们看看python字节码对于s[a] += b这种类型的解析import

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.存贮结果module

有结果能够看出前两个步骤成功执行了,在最后一步报错,由于s是不可变的序列因此s[a] 赋值失败报错,经过这个例子咱们能够获得的结论以下:
1.尽可能不要把可变对象放到元组里面(经过extend方法能够避免这个问题)
2.增量赋值 操做不是原子操做,由于报错了,可是仍是完成了 += 操做
这个问题是个不常碰见的边界问题,不常见,可是了解一下仍是颇有用处的~原理