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

时间:2016-07-13 06:36:22

标签: 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)
>>> 

3 个答案:

答案 0 :(得分:17)

在IRC上找到答案。

t[0] += [1]是几个不连续的行为:

  1. 正在加载t[0]
  2. 在其中构建一个包含1的新列表
  3. [1]添加到任何t[0]
  4. 重新分配t[0]
  5. 似乎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. 元组的变异也是就地尝试的,但是元组是不可变的,导致例外。
    3. 为什么这在明显的视线中不可见?因为像我这样的n00b会期望t[0] += [1]一起成功或失败,因为它是python的一条短线。但事实并非如此。

答案 1 :(得分: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
  • t[0]的值放在堆栈顶部,BINARY_SUBSCR,在这种情况下是一个(可变)列表。
  • 堆栈顶部的值使用+= [1]对其执行INPLACE_ADD,其中在这种情况下,堆栈的顶部是指元组内的列表。
  • t[0]分配给堆栈顶部的是STORE_SUBSCR,由于t本身是一个不可变的元组,因此在 > +=分配已经发生。

答案 2 :(得分:2)

Python开发人员在此处写了一个官方解释:https://docs.python.org/2/faq/programming.html#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works

简短版本是+ =实际上做了两件事,一件接着另一件事:

  1. 取右边的东西并将其添加到左侧的变量
  2. 将结果放入左侧的变量
  3. 在这种情况下,步骤1起作用是因为你被允许向列表中添加内容(它们是可变的),但是第2步失败了,因为在创建它们之后你不能把东西放入元组中(元组是不可变的)。

    在一个真实的程序中,我建议你不要这样做,因为t [0] .extend(['c'])完全相同。