所以我有这段代码:
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])
这是预期的行为吗?
我意识到这不是一个很常见的用例。但是,虽然预期会出现错误,但我没想到列表会发生变化。
是的,这是预期的。
元组不能更改。元组(如列表)是指向其他对象的结构。它不在乎那些对象是什么。它们可以是字符串,数字,元组,列表或其他对象。
因此,对元组中包含的对象之一执行任何操作(包括在列表中追加到该对象)都与元组的语义无关。
(想象一下,如果您编写了一个类,该类上具有导致其内部状态发生变化的方法。您不会期望不可能根据对象的存储位置在对象上调用这些方法)。
或另一个例子:
>>> l1 = [1, 2, 3] >>> l2 = [4, 5, 6] >>> t = (l1, l2) >>> l3 = [l1, l2] >>> l3[1].append(7)
一个列表和一个元组引用的两个可变列表。我是否应该能够做最后一行(答案:是)。如果您认为答案是否定的,为什么不呢?应该t更改l3(答案:否)的语义。
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__调用返回的突变列表。
__iadd__
第二行尝试将列表分配回元组,这将失败。
因此,在程序结束时,列表已扩展,但是操作的第二部分+=失败。
+=