为什么 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]
,因为该操作会生成一个新元组而不是改变旧元组。
如果反汇编如下函数
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]
,因为该操作会生成一个新元组而不是改变旧元组。