【问题标题】:Why can you extend/append to a list in a tuple, but not assign to it? [duplicate]为什么您可以扩展/附加到元组中的列表,但不能分配给它? [复制]
【发布时间】:2017-02-01 04:14:42
【问题描述】:

灵感来自阅读footnote 4 of this article

考虑以下场景:

>>> t = (1,2, [3, 4])
>>> t[2] += [5,6]

Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module> 
    t[2] += [5,6]
TypeError: 'tuple' object does not support item assignment

元组是不可变的。因此,正如预期的那样,尝试添加到元组内的列表会引发错误。

但是,如果我们检查我们的元组,列表已添加到! (我可以想象这会导致很难追踪错误)

>>> t
(1, 2, [3, 4, 5, 6])

另外,两者都在扩展

>>> t[2].extend([7,8])
>>> t
(1, 2, [3, 4, 5, 6, 7, 8])

和附加

>>> t[2].append(9)
>>> t
(1, 2, [3, 4, 5, 6, 7, 8, 9])

工作不会引发错误。

所以,我的问题是:

  1. 如果元组是不可变的,为什么可以更改元组中的列表?
  2. 为什么第一个示例会引发错误,而其他两个则不会?
  3. 在第一个示例中,为什么即使引发错误,元组内的列表也会更改?

【问题讨论】:

  • 来自docs包含对可变对象的引用的不可变容器对象的值可以随着后者的值改变而改变;但是容器仍然被认为是不可变的,因为它包含的对象集合不能更改。因此,不变性与具有不可更改的值并不严格相同,它更微妙。
  • 查看相关documentation page
  • 对于 1. 参见:“为什么元组可以包含可变项?” stackoverflow.com/questions/9755990/…

标签: python tuples


【解决方案1】:

如果元组是不可变的,为什么可以更改元组中的列表?

因为“元组是不可变的”仅意味着您不能修改元组。从元组中引用的列表不是元组的一部分,它不“知道”它在元组中,也没有办法拒绝被修改。

为什么第一个示例会引发错误,而其他两个则不会?

因为+= 的工作原理。它在列表上调用__iadd__,然后(因为__iadd__ 不需要返回原始对象)尝试将生成的修改对象分配回元组。第一件事成功,第二件事失败。

也就是说,对于这种t[2] 具有__iadd__ 函数的情况,t[2] += [5,6] 相当于:

t[2] = t[2].__iadd__([5,6])

在第一个示例中,为什么即使引发错误,元组内的列表也会更改?

因为这个 Python 操作不提供 C++ 中我们所说的“强异常保证”。操作的第一部分已经执行,并且当第二部分失败时不能(或无论如何不能)逆转。正式版见Why does a_tuple[i] += [‘item’] raise an exception when the addition works?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-13
    • 2015-03-16
    • 2020-05-25
    • 2011-10-11
    相关资源
    最近更新 更多