附加到元组中定义的列表-是bug吗?[重复]

发布于 2021-01-29 14:10:30

这个问题已经在这里有了答案

可变容器内的可变类型 (3个答案)

3年前关闭。

所以我有这段代码:

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])

这是预期的行为吗?

注意

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

关注者
0
被浏览
118
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    是的,这是预期的。

    元组不能更改。元组(如列表)是指向其他对象的结构。它不在乎那些对象是什么。它们可以是字符串,数字,元组,列表或其他对象。

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

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

    或另一个例子:

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

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

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

    为什么会出错?

    本示例使用infix运算符:

    许多操作都有“就地”版本。与通常的语法相比,以下函数提供了对原位运算符的更原始的访问;例如,语句x + = y等效于x =
    operator.iadd(x,y)。另一种表达方式是说z = operator.iadd(x,y)等效于复合语句z = x; z + = 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__调用返回的突变列表。

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

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



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看