附加到元组中定义的列表 - 这是一个错误吗?

时间:2015-04-20 11:56:33

标签: python list tuples

所以我有这段代码:

tup = ([1,2,3],[7,8,9])
tup[0] += (4,5,6)

生成此错误:

TypeError: 'tuple' object does not support item assignment

这段代码:

tup = ([1,2,3],[7,8,9])
try:
    tup[0] += (4,5,6)
except TypeError:
    print tup

打印出来:

([1, 2, 3, 4, 5, 6], [7, 8, 9])

预计会出现这种情况吗?

注意

我意识到这不是一个非常常见的用例。但是,虽然预计会出现错误,但我没想到列表会发生变化。

4 个答案:

答案 0 :(得分:33)

是的,这是预料之中的。

无法更改元组。像列表一样的元组是指向其他对象的结构。它并不关心那些物体是什么。它们可以是字符串,数字,元组,列表或其他对象。

因此,对元组中包含的一个对象执行任何操作,包括如果它是列表,则附加到该对象,与元组的语义无关。

(想象一下,如果你编写了一个类,其上有方法会导致其内部状态发生变化。你不会指望根据它存储的位置在对象上调用这些方法是不可能的。)

或另一个例子:

>>> l1 = [1, 2, 3]
>>> l2 = [4, 5, 6]
>>> t = (l1, l2)
>>> l3 = [l1, l2]
>>> l3[1].append(7)

列表和元组引用的两个可变列表。我应该能够做最后一行(回答:是)。如果您认为答案是否定的,为什么不呢? t应改变l3的语义(答案:否)。

如果你想要一个连续结构的不可变对象,它应该一直是元组。

为什么会出错?

此示例使用中缀运算符:

  

许多操作都有“就地”版本。以下功能   提供比通常更原始的就地操作员访问   语法呢;例如,语句x + = y等于x =   operator.iadd(x,y)。另一种说法是z =   operator.iadd(x,y)等价于复合语句z = x; ž   + = y。

https://docs.python.org/2/library/operator.html

所以这个:

l = [1, 2, 3]
tup = (l,)
tup[0] += (4,5,6)

相当于:

l = [1, 2, 3]
tup = (l,)
x = tup[0]
x = x.__iadd__([4, 5, 6]) # like extend, but returns x instead of None
tup[0] = x

__iadd__行成功,并修改第一个列表。所以列表已经改变了。 __iadd__调用返回变异列表。

第二行尝试将列表分配回元组,这会失败。

因此,在程序结束时,列表已被扩展,但+=操作的第二部分失败。有关具体信息,请参阅this question

答案 1 :(得分:12)

我想tup[0] += (4, 5, 6)已被翻译为:

tup[0] = tup[0].__iadd__((4,5,6))
正常执行

tup[0].__iadd__((4,5,6))更改第一个元素中的列表。但由于元组是不可变的,因此赋值失败。

答案 2 :(得分:5)

无法直接更改元组,更正。但是,您可以通过引用更改元组的元素。像:

>>> tup = ([1,2,3],[7,8,9])
>>> l = tup[0]
>>> l += (4,5,6)
>>> tup
([1, 2, 3, 4, 5, 6], [7, 8, 9])

答案 3 :(得分:1)

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步失败了,因为在创建它们之后你不能把东西放入元组中(元组是不可变的)。

    在一个真实的程序中,我建议你不要执行try-except子句,因为tup[0].extend([4,5,6])完全相同。