不可变容器内的可变类型

发布于 2025-01-03 07:59:17 字数 818 浏览 1 评论 0原文

我对修改元组成员有点困惑。以下不起作用:

>>> thing = (['a'],)
>>> thing[0] = ['b']
TypeError: 'tuple' object does not support item assignment
>>> thing
(['a'],)

但这确实有效:

>>> thing[0][0] = 'b'
>>> thing
(['b'],)

也有效:

>>> thing[0].append('c')
>>> thing
(['b', 'c'],)

不起作用,但有效(哈?!):

>>> thing[0] += 'd'
TypeError: 'tuple' object does not support item assignment
>>> thing
(['b', 'c', 'd'],)

看似与上一个相同,但有效:

>>> e = thing[0]
>>> e += 'e'
>>> thing
(['b', 'c', 'd', 'e'],)

那么到底规则是什么在游戏中,你什么时候可以或不能修改元组内的某些内容?这似乎更像是禁止对元组成员使用赋值运算符,但最后两种情况让我感到困惑。

I'm a bit confused about modifying tuple members. The following doesn't work:

>>> thing = (['a'],)
>>> thing[0] = ['b']
TypeError: 'tuple' object does not support item assignment
>>> thing
(['a'],)

But this does work:

>>> thing[0][0] = 'b'
>>> thing
(['b'],)

Also works:

>>> thing[0].append('c')
>>> thing
(['b', 'c'],)

Doesn't work, and works (huh?!):

>>> thing[0] += 'd'
TypeError: 'tuple' object does not support item assignment
>>> thing
(['b', 'c', 'd'],)

Seemingly equivalent to previous, but works:

>>> e = thing[0]
>>> e += 'e'
>>> thing
(['b', 'c', 'd', 'e'],)

So what exactly are the rules of the game, when you can and can't modify something inside a tuple? It seems to be more like prohibition of using the assignment operator for tuple members, but the last two cases are confusing me.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

云归处 2025-01-10 07:59:17

您可以始终修改元组内的可变值。您看到的令人费解的行为

>>> thing[0] += 'd'

是由 += 引起的。 += 运算符执行就地加法,但执行赋值 - 就地加法仅适用于文件,但赋值会失败,因为元组是不可变的。这样想就

>>> thing[0] = thing[0] + 'd'

更好地解释了这一点。我们可以使用标准库中的dis模块来查看从两个表达式生成的字节码。使用 += 我们得到一个 INPLACE_ADD 字节码:

>>> def f(some_list):
...     some_list += ["foo"]
... 
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (some_list)
              3 LOAD_CONST               1 ('foo')
              6 BUILD_LIST               1
              9 INPLACE_ADD         
             10 STORE_FAST               0 (some_list)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        

使用 + 我们得到一个 BINARY_ADD

>>> def g(some_list):
...     some_list = some_list + ["foo"]
>>> dis.dis(g)
  2           0 LOAD_FAST                0 (some_list)
              3 LOAD_CONST               1 ('foo')
              6 BUILD_LIST               1
              9 BINARY_ADD          
             10 STORE_FAST               0 (some_list)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        

请注意,我们得到一个 <代码>STORE_FAST在两个地方。当您尝试存储回元组时,这是失败的字节码 - 之前的 INPLACE_ADD 工作正常。

这解释了为什么“不起作用,但有效”的情况将修改后的列表留在后面:元组已经具有对列表的引用:

>>> id(thing[0])
3074072428L

然后由 INPLACE_ADD修改列表STORE_FAST 失败:

>>> thing[0] += 'd'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

因此元组仍然具有对相同列表的引用,但列表已就地修改:

>>> id(thing[0])
3074072428L
>>> thing[0] 
['b', 'c', 'd']

You can always modify a mutable value inside a tuple. The puzzling behavior you see with

>>> thing[0] += 'd'

is caused by +=. The += operator does in-place addition but also an assignment — the in-place addition works just file, but the assignment fails since the tuple is immutable. Thinking of it like

>>> thing[0] = thing[0] + 'd'

explains this better. We can use the dis module from the standard library to look at the bytecode generated from both expressions. With += we get an INPLACE_ADD bytecode:

>>> def f(some_list):
...     some_list += ["foo"]
... 
>>> dis.dis(f)
  2           0 LOAD_FAST                0 (some_list)
              3 LOAD_CONST               1 ('foo')
              6 BUILD_LIST               1
              9 INPLACE_ADD         
             10 STORE_FAST               0 (some_list)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        

With + we get a BINARY_ADD:

>>> def g(some_list):
...     some_list = some_list + ["foo"]
>>> dis.dis(g)
  2           0 LOAD_FAST                0 (some_list)
              3 LOAD_CONST               1 ('foo')
              6 BUILD_LIST               1
              9 BINARY_ADD          
             10 STORE_FAST               0 (some_list)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        

Notice that we get a STORE_FAST in both places. This is the bytecode that fails when you try to store back into a tuple — the INPLACE_ADD that comes just before works fine.

This explains why the "Doesn't work, and works" case leaves the modified list behind: the tuple already has a reference to the list:

>>> id(thing[0])
3074072428L

The list is then modified by the INPLACE_ADD and the STORE_FAST fails:

>>> thing[0] += 'd'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

So the tuple still has a reference to the same list, but the list has been modified in-place:

>>> id(thing[0])
3074072428L
>>> thing[0] 
['b', 'c', 'd']
谁对谁错谁最难过 2025-01-10 07:59:17

您无法修改元组,但可以修改元组中包含的内容。列表(以及集合、字典和对象)是引用类型,因此元组中的“事物”只是一个引用 - 实际列表是一个可变对象它由该引用指向,并且可以在不更改引用本身的情况下进行修改。

( + ,)       <--- your tuple (this can't be changed)
  |
  |
  v
 ['a']       <--- the list object your tuple references (this can be changed)

thing[0][0] = 'b' 之后:

( + ,)       <--- notice how the contents of this are still the same
  |
  |
  v
 ['b']       <--- but the contents of this have changed

thing[0].append('c') 之后:

( + ,)       <--- notice how this is still the same
  |
  |
  v
 ['b','c']   <--- but this has changed again

+= 的原因code> 的错误在于它并不完全等同于 .append() - 它实际上先进行加法,然后进行赋值(并且赋值失败),而不是仅仅就地附加。

You can't modify the tuple, but you can modify the contents of things contained within the tuple. Lists (along with sets, dicts, and objects) are a reference type and thus the "thing" in the tuple is just a reference - the actual list is a mutable object which is pointed to by that reference and can be modified without changing the reference itself.

( + ,)       <--- your tuple (this can't be changed)
  |
  |
  v
 ['a']       <--- the list object your tuple references (this can be changed)

After thing[0][0] = 'b':

( + ,)       <--- notice how the contents of this are still the same
  |
  |
  v
 ['b']       <--- but the contents of this have changed

After thing[0].append('c'):

( + ,)       <--- notice how this is still the same
  |
  |
  v
 ['b','c']   <--- but this has changed again

The reason why += errors is that it's not completely equivalent to .append() - it actually does an addition and then an assignment (and the assignment fails), rather than merely appending in-place.

浮萍、无处依 2025-01-10 07:59:17

您无法替换元组的元素,但可以替换该元素的全部内容。这会起作用:

thing[0][:] = ['b']

You cannot replace an element of a tuple, but you can replace the entire contents of the element. This will work:

thing[0][:] = ['b']
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文