关于 Python 的一个问题

2016-07-25 15:15:24 +08:00
 strahe

早上补习黑魔法的时候,在知乎上看到一个问题:

>>> 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')
>>>

没搞明白这是为什么,来个明白人解释一下。

4048 次点击
所在节点    Python
30 条回复
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')
>>>
```
raphaelsoul
2016-07-25 15:34:45 +08:00
@raphaelsoul 就是说明,+=这个语法糖和普通的相加赋值是不一样的
eclipselu
2016-07-25 15:37:33 +08:00
我猜是那个 list 是个 reference

http://imgur.com/a/ojuuq
eclipselu
2016-07-25 15:38:34 +08:00
图没贴上

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
```
tasiguo
2016-07-25 15:49:59 +08:00
黑魔法只是好听点,按它自己的定义本身这就是一个 bug
SErHo
2016-07-25 15:58:36 +08:00
关键在那个 +=,这个是 INPLACE_ADD ,对于 list 而言,实现方式是相当于 extend ,原 list 已经更改后才进行 tuple 的赋值,然后报错。。如果使用 a[1] = a[1] + [4],这样在赋值时 a[1] 并没有改变,所以结果就正常咯。
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 已经操作了
dosin
2016-07-25 16:10:06 +08:00
tuple 不是不可改值吗
tairan2006
2016-07-25 16:14:26 +08:00
这应该是个 bug 才对... 不应该报错。
kaneg
2016-07-25 16:17:12 +08:00
@dosin
我的理解是: Python 存储的是对象的引用, tuple 不可变指的是其引用不可重写,但其引用所指向的对象是否可更改它就管不到了
am241
2016-07-25 16:21:33 +08:00
内部等价于多条语句,后面的语句(将结果回写给 a )报错了,前面的语句(列表加)正常执行。
jixiangqd
2016-07-25 17:37:05 +08:00
@eclipselu 你这是什么工具? 看上去好神奇
yufpga
2016-07-25 17:42:29 +08:00
改变前后 id(a[1]) 并未发生改变, 所以 a 其实并没有被改变, 改变的只是 a[1] 引用的列表对象
eclipselu
2016-07-25 18:15:51 +08:00
@jixiangqd http://www.pythontutor.com/ 是这个,顺便推荐一下他写的书 The PhD Grind ,挺值得一读。
VicYu
2016-07-25 18:33:25 +08:00
你试试 a[1].extend([5])就知道了
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
zxc111
2016-07-25 19:47:19 +08:00
jixiangqd
2016-07-25 20:11:56 +08:00
@eclipselu 强!谢谢
WhiteT
2016-07-25 20:16:16 +08:00
@eclipselu 非常感谢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/294777

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX