不可变容器内的可变类型 [英] a mutable type inside an immutable container

查看:30
本文介绍了不可变容器内的可变类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

<预><代码>>>>东西 = (['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'

是由 += 引起的.+= 操作符进行就地加法,但赋值——就地加法只对文件起作用,但赋值失败,因为元组是不可变的.这么想

<预><代码>>>>东西[0] = 东西[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 19 INPLACE_ADD10 STORE_FAST 0 (some_list)13 LOAD_CONST 0 (无)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 19 BINARY_ADD10 STORE_FAST 0 (some_list)13 LOAD_CONST 0 (无)16 RETURN_VALUE

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

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

<预><代码>>>>id(东西[0])3074072428L

列表随后被 INPLACE_ADD 修改,STORE_FAST 失败:

<预><代码>>>>东西[0] + = 'd'回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中类型错误:元组"对象不支持项目分配

所以元组仍然有对相同列表的引用,但列表已经就地修改:

<预><代码>>>>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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆