一尘不染

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

python

所以我有这段代码:

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

这是预期的行为吗?

注意

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


阅读 201

收藏
2021-01-20

共1个答案

一尘不染

是的,这是预期的。

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

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

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

或另一个例子:

>>> 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__调用返回的突变列表。

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

因此,在程序结束时,列表已扩展,但是操作的第二部分+=失败。

2021-01-20