不可变容器内的可变类型

问题描述 投票:12回答:3

我对修改元组成员有些困惑。以下无效:

>>> 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'],)

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

python list tuples immutability mutable
3个回答
11
投票

您可以总是修改元组中的可变值。您通过

看到的令人困惑的行为
>>> thing[0] += 'd'

+=引起。 +=运算符执行就地加法,但alsoassignment进行就地加法-仅就文件而言,就地加法有效,但由于元组是不可变的,因此分配失败。想起来就像

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

对此进行更好的解释。我们可以使用标准库中的dis module查看由两个表达式生成的字节码。使用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

注意,我们在both都获得了>>> 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

因此元组仍然具有对[[same列表的引用,但是该列表已就地修改:

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

4
投票
您不能修改元组,但是可以修改元组中包含的内容。列表(以及集合,字典和对象)是

引用类型,因此“事物” in元组只是一个引用-实际列表是该对象指向的可变对象参考,并且可以在不更改参考本身的情况下进行修改。

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

( + ,) <--- 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 错误的原因是它并不完全等同于+=-它实际上是进行加法然后是赋值(并且赋值失败),而不是仅就地附加。

1
投票
您不能替换元组的元素,但是可以替换该元素的全部内容。这将起作用:

.append()

© www.soinside.com 2019 - 2024. All rights reserved.