不可变容器内的可变类型 [英] a mutable type inside an immutable container
问题描述
我对修改元组成员有点困惑.以下不起作用:
<预><代码>>>>东西 = (['a'],)>>>东西[0] = ['b']类型错误:元组"对象不支持项目分配>>>事物(['一种'],)但这确实有效:
<预><代码>>>>东西[0][0] = 'b'>>>事物(['b'],)也有效:
<预><代码>>>>thing[0].append('c')>>>事物(['b', 'c'],)不起作用,但有效(嗯?!):
<预><代码>>>>东西[0] + = 'd'类型错误:元组"对象不支持项目分配>>>事物(['b', 'c', 'd'],)看似与之前的相同,但有效:
<预><代码>>>>e = 事物[0]>>>e + = 'e'>>>事物(['b', 'c', 'd', 'e'],)那么游戏的规则到底是什么,什么时候可以修改元组中的内容?这似乎更像是禁止对元组成员使用赋值运算符,但最后两种情况让我感到困惑.
您可以始终修改元组内的可变值.你用
看到的令人费解的行为<预><代码>>>>东西[0] + = 'd' 是由 +=
引起的.+=
操作符进行就地加法,但也赋值——就地加法只对文件起作用,但赋值失败,因为元组是不可变的.这么想
更好地解释了这一点.我们可以使用标准库中的dis
模块来查看在从两个表达式生成的字节码处.使用 +=
我们得到一个 INPLACE_ADD
字节码:
使用 +
我们得到一个 BINARY_ADD
:
请注意,我们在两个的地方都得到了一个STORE_FAST
.这是当您尝试存储回元组时失败的字节码 - 之前出现的 INPLACE_ADD
工作正常.
这解释了为什么不起作用,但有效"的情况将修改后的列表留在后面:元组已经引用了列表:
<预><代码>>>>id(东西[0])3074072428L列表随后被 INPLACE_ADD
修改,STORE_FAST
失败:
所以元组仍然有对相同列表的引用,但列表已经就地修改:
<预><代码>>>>id(东西[0])3074072428L>>>东西[0]['b', 'c', 'd']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.
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']
这篇关于不可变容器内的可变类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!