早上补习黑魔法的时候,在知乎上看到一个问题:
>>> a = (1, [1,2,3], 'a')
>>> a[1] += [4]
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> a
(1, [1, 2, 3, 4], 'a')
>>>
没搞明白这是为什么,来个明白人解释一下。
|  |      1raphaelsoul      2016-07-25 15:33:45 +08:00 ``` >>> [1,2,3] + [4] [1, 2, 3, 4] >>> a = (1, [1,2,3], 'a') >>> a[1] += [4] Traceback (most recent call last): File "<pyshell#2>", line 1, in <module> a[1] += [4] TypeError: 'tuple' object does not support item assignment >>> a (1, [1, 2, 3, 4], 'a') >>> a[1] = a[1] + [5] Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> a[1] = a[1] + [5] TypeError: 'tuple' object does not support item assignment >>> a (1, [1, 2, 3, 4], 'a') >>> ``` | 
|  |      2raphaelsoul      2016-07-25 15:34:45 +08:00 @raphaelsoul 就是说明,+=这个语法糖和普通的相加赋值是不一样的 | 
|  |      3eclipselu      2016-07-25 15:37:33 +08:00 | 
|  |      4eclipselu      2016-07-25 15:38:34 +08:00 | 
|  |      5eclipselu      2016-07-25 15:41:09 +08:00 ```python # a = (1, [1,2,3], 'a') l = [1, 2, 3] a = (1, l, 'a') # a[1] += [4] l += [4] a[1] = l ``` | 
|      6tasiguo      2016-07-25 15:49:59 +08:00 黑魔法只是好听点,按它自己的定义本身这就是一个 bug | 
|  |      7SErHo      2016-07-25 15:58:36 +08:00  1 关键在那个 +=,这个是 INPLACE_ADD ,对于 list 而言,实现方式是相当于 extend ,原 list 已经更改后才进行 tuple 的赋值,然后报错。。如果使用  a[1] = a[1] + [4],这样在赋值时 a[1] 并没有改变,所以结果就正常咯。 | 
|  |      8trdcaz      2016-07-25 16:05:19 +08:00 >>> a = (1, [1, 2, 3], 'a') >>> b = a[1] >>> b += [4] >>> a (1, [1, 2, 3, 4], 'a') >>> 里面的[1, 2, 3]是一个 reference ,可以改变 我猜+=的报错是针对 a ( tuple ),但是实际上里面的 list 已经操作了 | 
|      9dosin      2016-07-25 16:10:06 +08:00 via iPhone tuple 不是不可改值吗 | 
|      10tairan2006      2016-07-25 16:14:26 +08:00 这应该是个 bug 才对... 不应该报错。 | 
|      11kaneg      2016-07-25 16:17:12 +08:00 @dosin  我的理解是: Python 存储的是对象的引用, tuple 不可变指的是其引用不可重写,但其引用所指向的对象是否可更改它就管不到了 | 
|      12am241      2016-07-25 16:21:33 +08:00 via Android 内部等价于多条语句,后面的语句(将结果回写给 a )报错了,前面的语句(列表加)正常执行。 | 
|      14yufpga      2016-07-25 17:42:29 +08:00 改变前后 id(a[1]) 并未发生改变, 所以 a 其实并没有被改变, 改变的只是 a[1] 引用的列表对象 | 
|  |      15eclipselu      2016-07-25 18:15:51 +08:00  4 @jixiangqd http://www.pythontutor.com/ 是这个,顺便推荐一下他写的书  The PhD Grind ,挺值得一读。 | 
|      16VicYu      2016-07-25 18:33:25 +08:00 你试试 a[1].extend([5])就知道了 | 
|      17WangYanjie      2016-07-25 18:38:57 +08:00 @raphaelsoul 一样的, 第一步 修改 list 不报错,第二部,给 tuple 的第二个元素赋值,报错; >>> a = (1, [2, 3, 4], 'a') >>> b = a[1] >>> b += [4] >>> a[1] = b Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment | 
|  |      18zxc111      2016-07-25 19:47:19 +08:00  2 | 
|  |      2221grams      2016-07-25 20:22:45 +08:00 妥妥的就是个 bug ,要么就别报错,要报错就别修改值 | 
|      23prm      2016-07-25 23:03:36 +08:00 并不是一个 Bug ,来看一下 byte code $ py -3 Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> from dis import dis >>> dis(''' ... a = (1, [1,2,3], 'a') ... a[1] += [4] ... ''') 2 0 LOAD_CONST 0 (1) 3 LOAD_CONST 0 (1) 6 LOAD_CONST 1 (2) 9 LOAD_CONST 2 (3) 12 BUILD_LIST 3 15 LOAD_CONST 3 ('a') 18 BUILD_TUPLE 3 21 STORE_NAME 0 (a) 3 24 LOAD_NAME 0 (a) 27 LOAD_CONST 0 (1) 30 DUP_TOP_TWO 31 BINARY_SUBSCR 32 LOAD_CONST 4 (4) 35 BUILD_LIST 1 38 INPLACE_ADD 39 ROT_THREE 40 STORE_SUBSCR 41 LOAD_CONST 5 (None) 44 RETURN_VALUE a[1] += [4]实际是两部操作,+= 和 []= INPLACE_ADD 是 += STORE_SUBSCR 是 []= 先发生了+=,然后 []= 报错了。仅此而已。 | 
|      24prm      2016-07-25 23:08:01 +08:00 归根结底这是 Python 语言设计的锅, python 定义了与各种 operator 等价的 magic method (双下划线 method ,比如__iadd__是+=,当然,也就是后来编译出来的 INPLACE_ADD),当你从语法上玩弄语义的时候(+=, []= 合并成了 x[y]+=z ),就被自己玩了。。 | 
|  |      25fzinfz      2016-07-26 01:23:53 +08:00 为什么都在关心 += 。。。 a = (1,2) a[1] = 2 同样的报错 Tuples are immutable. | 
|  |      26Ahri      2016-07-26 04:33:40 +08:00 @prm 是对的。 说原因是 tuples are immutable 的没看懂这个问题。 建议类似操作用 append 或 extend ,不报错。 >>> a=(1,[2]) >>> a[1] [2] >>> a[1].append(2) >>> a (1, [2, 2]) | 
|  |      29jixiangqd      2016-07-26 11:08:50 +08:00 突然想明白了为啥+= 要有个[]=的操作。 为了实现 a = [1];a[0]+=1 这种基本类型在容器内执行执行+= | 
|  |      30wizardoz      2016-07-26 13:21:57 +08:00 赶紧去提 bug |