早上补习黑魔法的时候,在知乎上看到一个问题:
>>> 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')
>>>
没搞明白这是为什么,来个明白人解释一下。
1
raphaelsoul 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') >>> ``` |
2
raphaelsoul 2016-07-25 15:34:45 +08:00
@raphaelsoul 就是说明,+=这个语法糖和普通的相加赋值是不一样的
|
3
eclipselu 2016-07-25 15:37:33 +08:00
|
4
eclipselu 2016-07-25 15:38:34 +08:00
|
5
eclipselu 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 ``` |
6
tasiguo 2016-07-25 15:49:59 +08:00
黑魔法只是好听点,按它自己的定义本身这就是一个 bug
|
7
SErHo 2016-07-25 15:58:36 +08:00 1
关键在那个 +=,这个是 INPLACE_ADD ,对于 list 而言,实现方式是相当于 extend ,原 list 已经更改后才进行 tuple 的赋值,然后报错。。如果使用 a[1] = a[1] + [4],这样在赋值时 a[1] 并没有改变,所以结果就正常咯。
|
8
trdcaz 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 已经操作了 |
9
dosin 2016-07-25 16:10:06 +08:00 via iPhone
tuple 不是不可改值吗
|
10
tairan2006 2016-07-25 16:14:26 +08:00
这应该是个 bug 才对... 不应该报错。
|
11
kaneg 2016-07-25 16:17:12 +08:00
@dosin
我的理解是: Python 存储的是对象的引用, tuple 不可变指的是其引用不可重写,但其引用所指向的对象是否可更改它就管不到了 |
12
am241 2016-07-25 16:21:33 +08:00 via Android
内部等价于多条语句,后面的语句(将结果回写给 a )报错了,前面的语句(列表加)正常执行。
|
14
yufpga 2016-07-25 17:42:29 +08:00
改变前后 id(a[1]) 并未发生改变, 所以 a 其实并没有被改变, 改变的只是 a[1] 引用的列表对象
|
15
eclipselu 2016-07-25 18:15:51 +08:00 4
@jixiangqd http://www.pythontutor.com/ 是这个,顺便推荐一下他写的书 The PhD Grind ,挺值得一读。
|
16
VicYu 2016-07-25 18:33:25 +08:00
你试试 a[1].extend([5])就知道了
|
17
WangYanjie 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 |
18
zxc111 2016-07-25 19:47:19 +08:00 2
|
22
21grams 2016-07-25 20:22:45 +08:00
妥妥的就是个 bug ,要么就别报错,要报错就别修改值
|
23
prm 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 是 []= 先发生了+=,然后 []= 报错了。仅此而已。 |
24
prm 2016-07-25 23:08:01 +08:00
归根结底这是 Python 语言设计的锅, python 定义了与各种 operator 等价的 magic method (双下划线 method ,比如__iadd__是+=,当然,也就是后来编译出来的 INPLACE_ADD),当你从语法上玩弄语义的时候(+=, []= 合并成了 x[y]+=z ),就被自己玩了。。
|
25
fzinfz 2016-07-26 01:23:53 +08:00
为什么都在关心 += 。。。
a = (1,2) a[1] = 2 同样的报错 Tuples are immutable. |
26
Ahri 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]) |
29
jixiangqd 2016-07-26 11:08:50 +08:00
突然想明白了为啥+= 要有个[]=的操作。
为了实现 a = [1];a[0]+=1 这种基本类型在容器内执行执行+= |
30
wizardoz 2016-07-26 13:21:57 +08:00
赶紧去提 bug
|