美文网首页
记一次+=与extend

记一次+=与extend

作者: 倔犟的贝壳 | 来源:发表于2021-07-05 22:46 被阅读0次

    看《流利的python》刷到的,记录一下
    python代码如下:

    #定义一个元组,元组的第2个元素是数组
    a = (1,2,[3,4])
    #定义一个数组
    b = [5,6]
    

    先把两个的内存地址打印出来

    print("a的内存地址为:%s" %(id(a)))
    print("a[2]的内存地址为:%s" %(id(a[2])))
    
    a的内存地址为:140632039863680
    a[2]的内存地址为:140632038209792
    
    a[2] += b
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-35-1f2c16bcdd7a> in <module>
    ----> 1 a[2] += b
          2 #print(id(a[2]+b))
    
    TypeError: 'tuple' object does not support item assignment
    

    此时会报错,元组对象不可赋值。但是打印a[2],a[2] 是已经被修改了的

    print(a[2])
    
    (1, 2, [3, 4, 5, 6])
    

    但若是换成

    a[2].extend(b)
    print(a[2])
    

    则不会报错,正常运行,输出a[2]

    (1, 2, [3, 4, 5, 6])
    

    为什么+=会报错,而extend不会报错呢?
    我们先通过dis来查看一下a[2]+=b的字节码:

                  0 LOAD_GLOBAL              0 (a)
                  2 LOAD_CONST               1 (2)
                  4 DUP_TOP_TWO
                  6 BINARY_SUBSCR
                  8 LOAD_GLOBAL              1 (b)
                 10 INPLACE_ADD
                 12 ROT_THREE
                 14 STORE_SUBSCR
                 16 LOAD_CONST               0 (None)
                 18 RETURN_VALUE
    

    再查看extend的字节码:

                 0 LOAD_GLOBAL              0 (a)
                  2 LOAD_CONST               1 (2)
                  4 BINARY_SUBSCR
                  6 LOAD_ATTR                1 (extend)
                  8 LOAD_GLOBAL              2 (b)
                 10 BINARY_SUBSCR
                 12 POP_TOP
                 14 LOAD_CONST               0 (None)
                 16 RETURN_VALUE
    

    通过字节码可以看到,+= 操作虽然使列表原地扩展了,但是当其保存的时候,a[2] = TOS的时候(STORE_SUBSCR),就会报错,元组对象不可赋值。而extend只是原地扩展序列,没有保存操作,所以不会报错。

    +=背后的特殊方法是__iadd__("就地加法”,不会产生中间变量),如果一个类没有实现这个方法的话,python就会调用__add__方法(会产生中间变量)。对于可变序列,一般都实现了iadd_,而对于不可变序列,没有实现__iadd__。
    元组不可变,而数组是可变的。所以当调用a[2]的时候,首先取出a[2]的元素是一个数组[3,4],存入TOS(栈的顶端),对这个数组进行+=操作,调用的__iadd__直接扩充数组为[3,4,5,6],然后需要把这个数组重新保存到元组,a[2] = TOS。但元组是不可变序列,所以赋值失败。

    相关文章

      网友评论

          本文标题:记一次+=与extend

          本文链接:https://www.haomeiwen.com/subject/khaiultx.html