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

Python 附加到元组中定义的列表-这是错误吗?,python,list,tuples,Python,List,Tuples,所以我有这个代码: tup = ([1,2,3],[7,8,9]) tup[0] += (4,5,6) tup = ([1,2,3],[7,8,9]) try: tup[0] += (4,5,6) except TypeError: print tup 这将生成此错误: TypeError: 'tuple' object does not support item assignment 而此代码: tup = ([1,2,3],[7,8,9]) tup[0] += (4,5

所以我有这个代码:

tup = ([1,2,3],[7,8,9])
tup[0] += (4,5,6)
tup = ([1,2,3],[7,8,9])
try:
    tup[0] += (4,5,6)
except TypeError:
    print tup
这将生成此错误:

TypeError: 'tuple' object does not support item assignment
而此代码:

tup = ([1,2,3],[7,8,9])
tup[0] += (4,5,6)
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])
这是预期的行为吗

注 我意识到这不是一个非常常见的用例。然而,虽然错误是意料之中的,但我没有预料到列表会发生更改

是的,这是意料之中的

无法更改元组。元组类似于列表,是指向其他对象的结构。它不关心这些对象是什么。它们可以是字符串、数字、元组、列表或其他对象

因此,对元组中包含的一个对象执行任何操作(包括在该对象是列表时附加到该对象),都与元组的语义无关

(想象一下,如果您编写了一个类,该类上的方法会导致其内部状态发生变化。您不会期望无法根据对象的存储位置对其调用这些方法)

或者另一个例子:

>>> l1 = [1, 2, 3]
>>> l2 = [4, 5, 6]
>>> t = (l1, l2)
>>> l3 = [l1, l2]
>>> l3[1].append(7)
一个列表和一个元组引用的两个可变列表。我是否能完成最后一行(回答:是)。如果你认为答案是否定的,为什么不呢?如果
t
更改
l3
的语义(回答:否)

如果您想要一个顺序结构的不可变对象,那么它应该一直是元组

为什么会出错? 此示例使用中缀运算符:

许多操作都有“就地”版本。以下功能 为现场操作员提供比通常更原始的访问 语法确实如此;例如,语句x+=y相当于x= 运算符.iadd(x,y)。另一种说法是z= 运算符.iadd(x,y)等价于复合语句z=x;Z +=y

因此:

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
\uuu iadd\uu
行成功,并修改第一个列表。所以名单已经改变了。
\uuuu iadd\uuuu
调用返回变异列表

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


因此,在程序结束时,列表已扩展,但
+=
操作的第二部分失败。有关详细信息,请参见。

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

tup[0] = tup[0].__iadd__((4,5,6))

tup[0]。\uuuu iadd\uuuu((4,5,6))
正常执行,更改第一个元素中的列表。但是分配失败,因为元组是不可变的。

元组不能直接更改,对吗。但是,您可以通过引用更改元组的元素。比如:

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

Python开发人员就为什么会出现这种情况写了一份官方解释:

简而言之,+=实际上做了两件事,一件接一件:

  • 往右边跑
  • 将结果分配给左侧的变量
  • 在这种情况下,第1步可以工作,因为您可以向列表中添加内容(它们是可变的),但第2步失败,因为您无法在创建内容后将其放入元组(元组是不可变的)


    在实际的程序中,我建议您不要使用try-except子句,因为
    tup[0].extend([4,5,6])
    执行完全相同的操作。

    事实上,您的第一次尝试显示了一个错误,但有效:
    >tup[0]
    向我返回
    [1,2,3,4,5,6]
    。重新编辑:这可能不是常见的用例,但是你必须理解其中的原因,这是非常重要的;非常好……那么,当它工作时,为什么会出错?@Pureferret:it“工作”是因为在列表上调用
    \uuuIdd\uuIdd
    成功地改变了列表。但将变异列表分配回元组的步骤失败了。看起来它成功了,因为元组已经包含了对列表的引用。@StevenRumbalski ahhh,列表是一个视图,不需要重新分配,但还是发生了(导致错误)。我认为您的拼写错误
    tup0]=x
    您可能希望在下一次修订中更正这是绝对正确的。我们可以通过使用
    dis
    模块看到这一点。给定
    def(tup):tup[0]+=(4,5,6)
    。如果我们使用
    dis.dis(f)
    。字节码中包含就地添加和存储子程序。就地添加对应于对象((4,5,6))。这个变异步骤成功了。STORE_SUBSCR是将结果分配给tuple
    tup[0]=u idadd的result_的位置。这会失败,因为元组无法修改。