元组:+ =运算符抛出异常,但是成功了吗?

Neh*_*ani 17 python tuples

为什么以下抛出异常,虽然它成功了?

>>> t = ([1, 2, 3], 4)
>>> t[0] += [1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
([1, 2, 3, 1], 4)
>>> 
Run Code Online (Sandbox Code Playgroud)

Neh*_*ani 17

在IRC上找到了答案.

t[0] += [1] 是几个离散的动作:

  1. 装载 t[0]
  2. 1它来建立一个新的列表
  3. 补充说,[1]到什么t[0]
  4. 重新分配 t[0]

这似乎x += y基本上是x = x + y(但是,是吗?)

棘手的一点+=意味着分配给元组t和列表t[0]

t[0] += [1]不是字面意思 t[0] = t[0] + [1],它是:t[0] = t[0].__iadd__([1])

真正发生的是:

  1. __iadd__都会改变列表并返回它.所以列表(它是第一个元素t)已经1附加到它上面了.
  2. tuple的变异也是就地尝试的,但是元组是不可变的,导致异常.

为什么这在明显的视线中不可见?因为像我这样的n00b会期望t[0] += [1]一起成功或失败,因为它是python的一条短线.但情况并非总是如此.

  • 这是一个很好的问题,也是一个很好的答案.来自我的+1.另外,有关"a + = b"vs"a = a + b"的更多背景信息,请参阅[此答案](http://stackoverflow.com/questions/15376509/when-is-ix-different-from-iix -in-python/15376520#15376520)(免责声明 - 我写了一个答案). (2认同)

mir*_*ulo 5

通过查看字节码,它也可以帮助理解这种行为dis.dis.

In[5]: dis('t[0] += [1]')
  1           0 LOAD_NAME                0 (t)
              3 LOAD_CONST               0 (0)
              6 DUP_TOP_TWO
              7 BINARY_SUBSCR
              8 LOAD_CONST               1 (1)
             11 BUILD_LIST               1
             14 INPLACE_ADD
             15 ROT_THREE
             16 STORE_SUBSCR
             17 LOAD_CONST               2 (None)
             20 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
  • 值的值t[0]放在堆栈顶部,BINARY_SUBSCR在这种情况下是一个(可变的)列表.
  • 堆栈顶部的值已经+= [1]在其上执行INPLACE_ADD,在这种情况下,堆栈的顶部引用元组内的列表.
  • t[0]在堆栈顶部的赋值发生时STORE_SUBSCR,由于t本身是一个不可变的元组,因此失败,在赋值已经发生之后引发错误+=.