为什么 Python 在已经有 INPLACE_ADD 的情况下发出 STORE_SUBSTR?

Why do Python emit STORE_SUBSTR when there's already an INPLACE_ADD?

如果反汇编如下函数

def test():
    t = (1, 2, [30])
    t[2] += [40]
    return t

您会看到 t[2] += [40] 的相应字节码如下所示:

  3          18 LOAD_FAST                0 (t)
             21 LOAD_CONST               2 (2)
             24 DUP_TOPX                 2
             27 BINARY_SUBSCR
             28 LOAD_CONST               4 (40)
             31 BUILD_LIST               1
             34 INPLACE_ADD
             35 ROT_THREE
             36 STORE_SUBSCR

[40]连接到存储在t[2]中的列表在INPLACE_ADD之后,为什么Python决定添加一个STORE_SUBSCR

那是因为 t[2] += [40] 是这样工作的:

temp = t[2]
temp += [40] # INPLACE_ADD
t[2] = temp  # STORE_SUBSCR

Python 实际上并没有就地更新列表,值首先存储在一个临时变量中。

证明:

>>> lst  = [[1], [2], [3]]
>>> def func():
...     lst[0] = [100]
...     return [40]
...
>>> lst[0] += func()
>>> lst
[[1, 40], [2], [3]]  # Not [[100, 40], [2], [3]]

这是因为INPLACE_ADD只要求在可能的情况下进行操作;如果对象是不可变的或没有费心去实现 __iadd__INPLACE_ADD 就会退回到常规的非就地添加。如果代码是

t = [1, 2, (30)]
t[2] += (40,)

显然有必要将新元组存储回 t[2],因为该操作会生成一个新元组而不是改变旧元组。