V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
strahe
V2EX  ›  Python

关于 Python 的一个问题

  •  
  •   strahe · 2016-07-25 15:15:24 +08:00 · 4183 次点击
    这是一个创建于 3045 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

    先发生了+=,然后 []= 报错了。仅此而已。
    prm
        24
    prm  
       2016-07-25 23:08:01 +08:00
    归根结底这是 Python 语言设计的锅, python 定义了与各种 operator 等价的 magic method (双下划线 method ,比如__iadd__是+=,当然,也就是后来编译出来的 INPLACE_ADD),当你从语法上玩弄语义的时候(+=, []= 合并成了 x[y]+=z ),就被自己玩了。。
    fzinfz
        25
    fzinfz  
       2016-07-26 01:23:53 +08:00
    为什么都在关心 += 。。。

    a = (1,2)
    a[1] = 2

    同样的报错

    Tuples are immutable.
    Ahri
        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])
    fzinfz
        27
    fzinfz  
       2016-07-26 08:27:04 +08:00 via Android
    @fzinfz 我错了,确实要关心+=,之前没看懂问题(>﹏<)
    sudoz
        28
    sudoz  
       2016-07-26 09:55:24 +08:00
    @dosin tuple 里所引用的对象不可变,但是对象的值可变
    jixiangqd
        29
    jixiangqd  
       2016-07-26 11:08:50 +08:00
    突然想明白了为啥+= 要有个[]=的操作。
    为了实现 a = [1];a[0]+=1 这种基本类型在容器内执行执行+=
    wizardoz
        30
    wizardoz  
       2016-07-26 13:21:57 +08:00
    赶紧去提 bug
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3231 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 12:59 · PVG 20:59 · LAX 04:59 · JFK 07:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.