我正在阅读这篇有趣的帖子https://asmeurer.github.io/blog/posts/tuples/
脚注作者出示了这个例子
>>> t=1,2,[3,4]
>>> t
(1, 2, [3, 4])
>>> t[2]+=[5,6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
尽管Python已经引发了异常但它确实改变了元组
>>> t
(1, 2, [3, 4, 5, 6])
不确定这里发生了什么,这是一个错误吗?
2.7.10和3.5.1中的行为相同
答案 0 :(得分:3)
所以layouts
的行为有点奇怪。对于像整数这样的不可变对象,它必须为同一个名称分配一个新对象:
+=
对于可变类型,例如列表,对象在适当位置更改,但也返回要分配给同一名称的同一对象。第一步适用于你的元组,但不适用于第二步。
这就是为什么在扩展列表后引发异常的原因。
答案 1 :(得分:3)
这是因为+=
运算符(__iadd__
或内部添加)实际上会在分配发生后返回一些内容。在list
中,这会转换为extend
调用(或类似内容),因此在返回列表引用以分配给t[2]
之前,新项目已经进入,然后提出例外。现在,您检查可以看到它已添加的值。以下是演示此内容的最低代码:
>>> class AddIDemo(object):
... def __init__(self, items):
... self.items = list(items)
... def __iadd__(self, other):
... print('extending other %r' % other)
... self.items.extend(other.items)
... print('returning self to complete +=')
... return self
... def __repr__(self):
... return self.items.__repr__()
...
>>> demo = AddIDemo([1, 2])
>>> demo += AddIDemo([3, 4])
extending other [3, 4]
returning self to complete +=
>>> demo
[1, 2, 3, 4]
>>> t = 1, 2, demo
>>> t
(1, 2, [1, 2, 3, 4])
>>>
>>> t[2] += AddIDemo([5, 6])
extending other [5, 6]
returning self to complete +=
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(1, 2, [1, 2, 3, 4, 5, 6])
>>>
请注意,我添加了一些更多的print语句来显示正在调用的函数以及操作如何通过list
或+=
进行标准__iadd__
操作。 / p>
答案 2 :(得分:0)
这不是一个错误,但是在引发异常时你将留下的状态可能会令人困惑。
设计中的元组不得更改,但这仅适用于元组而不适用于它的项目。如果它包含可修改的项目,则可以修改该项目。在您的情况下,列表是可以修改的项目。
现在,+=
运算符或多或少地映射到+
,然后将其分配给原始变量。可以+
操作,您可以添加两个列表。出于优化原因,+
的实施方式与extend()
类似,并进行了更新。但是由于元组项目无法实现,因此赋值失败。
基本上有两个教训: