Python中的元组赋值,这是Python中的错误吗?

时间:2016-09-24 07:00:10

标签: python list python-2.7 python-3.x tuples

我正在阅读这篇有趣的帖子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中的行为相同

3 个答案:

答案 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()类似,并进行了更新。但是由于元组项目无法实现,因此赋值失败。

基本上有两个教训:

  1. Python操作符可以按类型实现,并且可能表现得与普通的期望不同。
  2. 如果在调用函数时引发异常而未正确处理,则可能会先执行某些操作,之前不会进行清理。