V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
blacklinux
V2EX  ›  问与答

一个元祖的元素是列表的情况下修改元祖中的某个列表,会报错,但是元素修改会成功是什么原因

  •  
  •   blacklinux · 2017-07-10 10:14:51 +08:00 · 3008 次点击
    这是一个创建于 2695 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设一个元祖 t = ([1],[1,2],[1,2,3]) 执行这些操作: t[1].append(3) # 成功

    t[1] += [3] # 报错 TypeError: 'tuple' object does not support item assignment,但是 t 的已经被修改成([1], [1, 2, 3], [1, 2, 3])

    t[1] = t[1] + [3] # 报错,并且修改失败

    这是为什么呢?

    11 条回复    2017-07-10 11:49:17 +08:00
    freetstar
        1
    freetstar  
       2017-07-10 10:22:47 +08:00
    假设一个元祖 t = ([1],[1,2],[1,2,3]) 执行这些操作:t[1].append(3) # 成功

    t[1]此时是[1,2]这个列表,列表 append 操作 ok

    -------------------------------------------------------------------------------------

    t[1] += [3] # 报错 TypeError: 'tuple' object does not support item assignment

    元组不支持赋值操作,这个可以理解。为什么能改掉列表里的内容,是不是和+=操作符有关。

    等大神来解答
    dozer47528
        2
    dozer47528  
       2017-07-10 10:26:02 +08:00
    首先,元组不能修改,这个报错很正常

    但为什么修改成功了呢!?

    看上去 python 对 array += array 的内部实现是用了 append, 而不是创建一个新的 array。
    这个要验证也很简单,看一下源码就行了。
    lln133208
        3
    lln133208  
       2017-07-10 10:28:11 +08:00
    流畅的 Python 中有讲这个问题
    sunbeams001
        4
    sunbeams001  
       2017-07-10 10:30:36 +08:00
    有讨论过这个问题
    /t/357570
    blacklinux
        5
    blacklinux  
    OP
       2017-07-10 10:31:06 +08:00
    @lln133208 这个问题要如何解答?
    billion
        6
    billion  
       2017-07-10 10:31:22 +08:00
    元组一旦创建就不能修改。任何尝试直接修改元组的行为都会报错。

    但是,在 Python 中,列表是这种结构,你保存到元组中的是一个列表的 [引用] , [相当]于一个指针,它指向存放这个列表的实际位置。所以当你用 append 的时候,修改的是那个实际的列表,而这个“指针”不变。但是当你使用+号的时候,你相当于创建了一个新的列表,然后想把元组的下标为 1 的元素修改为新的“指针”,所以这个就是修改元组的行为。
    dozer47528
        7
    dozer47528  
       2017-07-10 10:33:48 +08:00
    看过源码了:


    所以直接操作原数组的,并没有创建一个新的,所以出现了这种状况。
    lln133208
        8
    lln133208  
       2017-07-10 10:37:14 +08:00
    @blacklinux 书中的意思和 6 楼差不多,最后还给出了几个建议。
    blacklinux
        9
    blacklinux  
    OP
       2017-07-10 11:04:30 +08:00
    @billion 懂了:

    t[1].append(3) #是直接对列表进行修改,没有赋值,不报错

    t[1] += [3] # 先堆栈操作,然后在赋值,由于元祖不可更改,所以赋值会失败,但由于堆栈已经操作过了,所以列表的内容还是改变了

    t[1] = t[1] + [3] # 直接赋值,会失败
    ETiV
        10
    ETiV  
       2017-07-10 11:14:23 +08:00 via iPhone
    看标题愣了一下

    元祖不是买蛋糕的吗……
    qyc666
        11
    qyc666  
       2017-07-10 11:49:17 +08:00 via iPhone
    元祖……雪月饼?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4003 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 10:22 · PVG 18:22 · LAX 02:22 · JFK 05:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.