不可变容器内的可变类型
我对修改元组成员有点困惑。以下不起作用:
>>> 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您可以始终修改元组内的可变值。您看到的令人费解的行为
是由
+=
引起的。+=
运算符执行就地加法,但也执行赋值 - 就地加法仅适用于文件,但赋值会失败,因为元组是不可变的。这样想就更好地解释了这一点。我们可以使用标准库中的
dis
模块来查看从两个表达式生成的字节码。使用+=
我们得到一个INPLACE_ADD
字节码:使用
+
我们得到一个BINARY_ADD
:请注意,我们得到一个 <代码>STORE_FAST在两个地方。当您尝试存储回元组时,这是失败的字节码 - 之前的 INPLACE_ADD 工作正常。
这解释了为什么“不起作用,但有效”的情况将修改后的列表留在后面:元组已经具有对列表的引用:
然后由
INPLACE_ADD
和修改列表STORE_FAST 失败:
因此元组仍然具有对相同列表的引用,但列表已就地修改:
You can always modify a mutable value inside a tuple. The puzzling behavior you see with
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 likeexplains this better. We can use the
dis
module from the standard library to look at the bytecode generated from both expressions. With+=
we get anINPLACE_ADD
bytecode:With
+
we get aBINARY_ADD
: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 — theINPLACE_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:
The list is then modified by the
INPLACE_ADD
and theSTORE_FAST
fails:So the tuple still has a reference to the same list, but the list has been modified in-place:
您无法修改元组,但可以修改元组中包含的内容。列表(以及集合、字典和对象)是引用类型,因此元组中的“事物”只是一个引用 - 实际列表是一个可变对象它由该引用指向,并且可以在不更改引用本身的情况下进行修改。
thing[0][0] = 'b'
之后:thing[0].append('c')
之后:+=
的原因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.
After
thing[0][0] = 'b'
:After
thing[0].append('c')
: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.您无法替换元组的元素,但可以替换该元素的全部内容。这会起作用:
You cannot replace an element of a tuple, but you can replace the entire contents of the element. This will work: