tdoly bio photo

tdoly

喜爱爬山,徒步的IT小小鸟

Twitter Facebook Google+ Github

python修改元组中列表元素的问题

展示问题

在python 2.7版本中,修改一个元组中的列表,得到了错误提示,而值却更改成功。

具体操作

程序1:

 1 >>> a = ([],[])
 2 >>> id(a)
 3 140551915278776
 4 >>> a[0].append(1)
 5 >>> a
 6 ([1], [])
 7 >>> id(a)
 8 140551915278776
 9 >>> a[0] += [2]
10 Traceback (most recent call last):
11   File "<stdin>", line 1, in <module>
12 TypeError: 'tuple' object does not support item assignment
13 >>> a
14 ([1, 2], [])
15 >>> id(a)
16 140551915278776

我们知道tuple是不可变的,当这样操作:

程序2:

1 >>> a = (1, 2)
2 >>> a
3 (1, 2)
4 >>> a[0] = 10
5 Traceback (most recent call last):
6   File "<stdin>", line 1, in <module>
7 TypeError: 'tuple' object does not support item assignment
8 >>> a
9 (1, 2)

会得到一个TypeError的错误,而值是不会改变的。但是在程序1的操作中,使用list的“append”方法可以得到正确的结果,使用“+=”虽然内存地址没有改变,但是得到了错误提示,更加奇怪的是值却成功的修改了。这是怎么一回事?

原理分析

”+=”属于in-place操作,所以

a[0] += [2]

等效于

a[0].extend([2]) #(1)
a[0] = a[0] #(2)

在执行步骤(1)时,没有出错,值已经被更改;执行步骤(2)时,因为’a’是一个tuple,所以这个时候提示了错误信息。最后到结果就出现了程序一呈现到情况。

本质分析

从原理上已经说的通了,现在看看程序内部都发生了什么事情

 1 >>> dis.dis(compile('a[0] += [2]', '<string>', 'exec'))
 2   1           0 LOAD_NAME                0 (a)
 3               3 LOAD_CONST               0 (0)
 4               6 DUP_TOPX                 2
 5             ## 在堆中生成a[0]
 6               9 BINARY_SUBSCR       
 7              10 LOAD_CONST               1 (2)
 8              13 BUILD_LIST               1
 9             ## 运行+=
10              16 INPLACE_ADD         
11              17 ROT_THREE           
12             ## a[0]赋值一个新值(出错)
13              18 STORE_SUBSCR        
14              19 LOAD_CONST               2 (None)
15              22 RETURN_VALUE        
16 >>>

从dis的分析中可以看到,在堆栈中生成一个列表并成功地扩展。在执行STORE_SUBSCR会调用C函数的PyObject_SetItem,检测对象是否支持这个元素。这种情况下的对象是一个元组,So…

参考资料