不可变容器内的可变类型

wim*_*wim 10 python tuples list mutable immutability

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

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

但这确实有效:

>>> thing[0][0] = 'b'
>>> thing
(['b'],)
Run Code Online (Sandbox Code Playgroud)

还有效:

>>> thing[0].append('c')
>>> thing
(['b', 'c'],)
Run Code Online (Sandbox Code Playgroud)

不起作用,并且有效(嗯?!):

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

看似与以前相同,但有效:

>>> e = thing[0]
>>> e += 'e'
>>> thing
(['b', 'c', 'd', 'e'],)
Run Code Online (Sandbox Code Playgroud)

那么,当你能够并且不能修改元组内的某些内容时,游戏的规则到底是什么?它似乎更像禁止使用赋值成员的赋值运算符,但最后两个案例让我感到困惑.

Mar*_*ler 11

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

>>> thing[0] += 'd'
Run Code Online (Sandbox Code Playgroud)

是由...引起的+=.该+=运营商不就地此外但一个任务 -就地除了刚工作的文件,但由于元组是不可变的分配失败.想一想就好

>>> thing[0] = thing[0] + 'd'
Run Code Online (Sandbox Code Playgroud)

更好地解释了这个 我们可以使用标准库中的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        
Run Code Online (Sandbox Code Playgroud)

随着+我们得到一个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        
Run Code Online (Sandbox Code Playgroud)

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

这就解释了为什么"不工作,无效"的情况会使修改后的列表落后:元组已经有了对列表的引用:

>>> id(thing[0])
3074072428L
Run Code Online (Sandbox Code Playgroud)

该名单随后被修改INPLACE_ADDSTORE_FAST失败:

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

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

>>> id(thing[0])
3074072428L
>>> thing[0] 
['b', 'c', 'd']
Run Code Online (Sandbox Code Playgroud)


Amb*_*ber 5

您不能修改元组,但可以修改元组中包含的事物的内容。列表(与套,http://stardict.sourceforge.net/Dictionaries.php下载,和对象一起)是一个引用类型,因此“东西”该元组仅仅是一个参考-实际列表是其由参考指向和可在不改变被修改可变对象参考自身。

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

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

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

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

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

+=错误的原因是它并不完全等同于.append()- 它实际上做了一个添加然后一个赋值(并且赋值失败),而不仅仅是就地附加。