【问题标题】:Assignment to a mutable tuple component in python: a bug? a feature? [duplicate]在 python 中分配给可变元组组件:一个错误?一个特征? [复制]
【发布时间】:2013-04-26 07:35:09
【问题描述】:

我们知道 Python 元组是不可变的,很好。当我尝试更改元组组件的引用时,我得到了一个异常,正如预期的那样。出乎意料的是,无论异常如何,组件都会发生变化,而我原以为元组不变性保证了对象不会是可变的。

它是错误、功能还是 PEP?

In [6]: x=([1],)
In [7]: type(x)
Out[7]: tuple
In [8]: x[0]+=[2,3]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-a73186f99454> in <module>()
----> 1 x[0]+=[2,3]

TypeError: 'tuple' object does not support item assignment   
In [9]: x
Out[9]: ([1, 2, 3],)

【问题讨论】:

  • 你不是第一个!
  • 另请注意,要使元组可散列,它的所有组件都必须是可散列的。 hash((1,2,3)) 工作正常。 hash(([],[],[])) 将引发异常。

标签: python tuples immutability pep


【解决方案1】:

这里更简单:

tup = ([],[])
tup[0].append(0)
tup[1].append(1)
print tup

打印出来

([0],[1])

元组不变性意味着组成元组的对象不能更改为不同的对象。这并不意味着您不能修改它们的值。

现在,话虽如此,您发现了一个非常有趣(如果是这个词)的极端情况,基本上可以翻译为:

x = tup[0]
x += [2,3]
tup[0] = x

所以,前两行按预期工作,然后你得到一个异常。

【讨论】:

  • 我的意思是,尽管抛出了异常,但不可变对象会发生变化。在我的示例中,您有点不希望出现这种情况。
  • @p3t3 Nto 不可变对象已更改,但它的(可变)成员之一。那是有区别的。
  • 是的,当尝试更改不可变对象中的引用时会引发异常,见下文。
【解决方案2】:

有趣的点。

之所以会这样,是因为

x[0]+=[2,3]

翻译成

x[0] = x[0].__iadd__([2,3])

这意味着它首先调用__iadd__,它会修改列表,然后才尝试对元组执行非法分配。

(当然,解决方法很容易(例如@luispedro 的回答),但我知道您的问题不是关于如何解决它。)

它是错误、功能还是 PEP?

很难说。由于最小惊讶原则,我倾向于投票给“错误”。人们会期望x[0].extend(y) 的行为类似于a=x[0]; a.extend(y) 的行为,而a=x[0]; a+=y 的行为类似于x[0]+=y。

一个可能的修复(至少对于 python 内置类型)可能是要求__setitem__(self, k, v) 应转换为无操作以防self[k] is v。 (并且覆盖 __setitem__ 的自定义类应该遵守)。

【讨论】:

  • 是的,代码等价于 x[0] = x[0].__iadd__([2,3])。尽管出现异常,我仍然看不到初始引用如何更改.如何查看 list.__iadd__(self, other) 的代码?
  • 当执行像x[0] = x[0].__iadd__([2,3]) 这样的行时,它首先计算右侧的表达式 (__iadd__),从而修改列表对象。没有什么可以挽回的。只有这样它才会执行__setitem__ 赋值,这会引发错误。
  • @shx2 很好的解释,但对“错误”的投票不适合 IMO。这就是它的工作方式。
  • @shx2:知道了,谢谢,如果我可以投票,那就是答案。
猜你喜欢
  • 1970-01-01
  • 2016-05-01
  • 2021-04-11
  • 1970-01-01
  • 2014-07-14
  • 2023-02-09
  • 2018-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多