为什么 if 语句没有显示在操作码中?
why if-statement not shown in opcode?
这是一个简单的示例:
>>> def foo():
... if True:
... return 'yes'
...
>>> import dis
>>> dis.dis(foo)
3 0 LOAD_CONST 1 ('yes')
2 RETURN_VALUE
为什么这里没有关于if语句的字节码?它只是直接return值。
- CPython3.6
在 Python 3 中,True
不能被覆盖,所以 Python 被允许“优化”这个并假设 True
永远是真的。
由于历史原因,Python 2 没有 True
作为 constant/keyword,因此不允许语言对此进行优化:
Python 2.7.16 (default, May 8 2021, 11:48:02)
[GCC Apple LLVM 12.0.5 (clang-1205.0.19.59.6) [+internal-os, ptrauth-isa=deploy on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo():
... if True:
... return 'yes'
...
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (True)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 ('yes')
9 RETURN_VALUE
>> 10 LOAD_CONST 0 (None)
13 RETURN_VALUE
所以从历史上看,在 Python 2 中你可以(即使你永远不应该)设置 True = 213
甚至 True = 0
(或 True = False
)并弄乱你的程序逻辑:
>>> True = 0
>>> print(foo())
None
>>> True = 1
>>> print(foo())
yes
在Python3中,这是不可能的:
Python 3.9.5 (default, May 4 2021, 03:36:27)
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 213
File "<stdin>", line 1
True = 213
^
SyntaxError: cannot assign to True
请注意 if True
是一种特殊情况,因为 Python 3 无法优化其他重言式(还):
>>> def foo():
... if 3 == 3:
... return 'yes'
...
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (3)
2 LOAD_CONST 1 (3)
4 COMPARE_OP 2 (==)
6 POP_JUMP_IF_FALSE 12
3 8 LOAD_CONST 2 ('yes')
10 RETURN_VALUE
>> 12 LOAD_CONST 0 (None)
14 RETURN_VALUE
请注意,这是特定于实现的 (CPython),在 PyPy 3 中又略有不同;它可以计算出 if True
始终是 True
,但随后还会在函数末尾为隐式 return None
生成字节码(永远不会执行):
Python 3.7.10 (51efa818fd9b24f625078c65e8e2f6a5ac24d572, Apr 08 2021, 17:43:00)
[PyPy 7.3.4 with GCC Apple LLVM 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>> def foo():
.... if True:
.... return 'yes'
....
>>>> import dis
>>>> dis.dis(foo)
3 0 LOAD_CONST 1 ('yes')
2 RETURN_VALUE
4 LOAD_CONST 0 (None)
6 RETURN_VALUE
再次为了完整起见,具有 Python 2.7 语义的 PyPy 不允许优化掉 True
,因为它可以设置为其他东西(这里是隐式代码return None
是正确的,因为它可能会根据您设置 True
的内容执行):
Python 2.7.18 (63df5ef41012b07fa6f9eaba93f05de0eb540f88, Apr 08 2021, 15:53:40)
[PyPy 7.3.4 with GCC Apple LLVM 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>> def foo():
.... if True:
.... return 'yes'
....
>>>> import dis
>>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (True)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 ('yes')
9 RETURN_VALUE
>> 10 LOAD_CONST 0 (None)
13 RETURN_VALUE
在C中是如何实现的Python
优化在CPython 3 in optimize_basic_block()
in Python/compile.c
中实现,截至本文撰写时this line(“删除LOAD_CONST const;条件jump") 进行优化。换句话说,编译器生成这样的操作码:
LOAD_CONST ... (True)
POP_JUMP_IF_FALSE x
LOAD_CONST ... ('yes')
RETURN_VALUE
x LOAD_CONST ... (None)
RETURN_VALUE
所以如果它看到一个LOAD_CONST
后面跟着一个POP_JUMP_IF_FALSE
,并且它可以在编译时证明常量是True
,它会删除LOAD_CONST
并将 POP_JUMP_IF_FALSE
转换为 JUMP_ABSOLUTE
或 NOP
,具体取决于计算的布尔值(此处它将转换为 NOP
):
NOP
NOP
LOAD_CONST ... ('yes')
RETURN_VALUE
x LOAD_CONST ... (None)
RETURN_VALUE
进一步的步骤 (clean_basic_block()
) 将删除 NOP(无操作)并且可能额外的步骤将在 RETURN_VALUE
死后找出该代码并将其删除。
这是一个简单的示例:
>>> def foo():
... if True:
... return 'yes'
...
>>> import dis
>>> dis.dis(foo)
3 0 LOAD_CONST 1 ('yes')
2 RETURN_VALUE
为什么这里没有关于if语句的字节码?它只是直接return值。
- CPython3.6
在 Python 3 中,True
不能被覆盖,所以 Python 被允许“优化”这个并假设 True
永远是真的。
由于历史原因,Python 2 没有 True
作为 constant/keyword,因此不允许语言对此进行优化:
Python 2.7.16 (default, May 8 2021, 11:48:02)
[GCC Apple LLVM 12.0.5 (clang-1205.0.19.59.6) [+internal-os, ptrauth-isa=deploy on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def foo():
... if True:
... return 'yes'
...
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (True)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 ('yes')
9 RETURN_VALUE
>> 10 LOAD_CONST 0 (None)
13 RETURN_VALUE
所以从历史上看,在 Python 2 中你可以(即使你永远不应该)设置 True = 213
甚至 True = 0
(或 True = False
)并弄乱你的程序逻辑:
>>> True = 0
>>> print(foo())
None
>>> True = 1
>>> print(foo())
yes
在Python3中,这是不可能的:
Python 3.9.5 (default, May 4 2021, 03:36:27)
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 213
File "<stdin>", line 1
True = 213
^
SyntaxError: cannot assign to True
请注意 if True
是一种特殊情况,因为 Python 3 无法优化其他重言式(还):
>>> def foo():
... if 3 == 3:
... return 'yes'
...
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (3)
2 LOAD_CONST 1 (3)
4 COMPARE_OP 2 (==)
6 POP_JUMP_IF_FALSE 12
3 8 LOAD_CONST 2 ('yes')
10 RETURN_VALUE
>> 12 LOAD_CONST 0 (None)
14 RETURN_VALUE
请注意,这是特定于实现的 (CPython),在 PyPy 3 中又略有不同;它可以计算出 if True
始终是 True
,但随后还会在函数末尾为隐式 return None
生成字节码(永远不会执行):
Python 3.7.10 (51efa818fd9b24f625078c65e8e2f6a5ac24d572, Apr 08 2021, 17:43:00)
[PyPy 7.3.4 with GCC Apple LLVM 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>> def foo():
.... if True:
.... return 'yes'
....
>>>> import dis
>>>> dis.dis(foo)
3 0 LOAD_CONST 1 ('yes')
2 RETURN_VALUE
4 LOAD_CONST 0 (None)
6 RETURN_VALUE
再次为了完整起见,具有 Python 2.7 语义的 PyPy 不允许优化掉 True
,因为它可以设置为其他东西(这里是隐式代码return None
是正确的,因为它可能会根据您设置 True
的内容执行):
Python 2.7.18 (63df5ef41012b07fa6f9eaba93f05de0eb540f88, Apr 08 2021, 15:53:40)
[PyPy 7.3.4 with GCC Apple LLVM 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>> def foo():
.... if True:
.... return 'yes'
....
>>>> import dis
>>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (True)
3 POP_JUMP_IF_FALSE 10
3 6 LOAD_CONST 1 ('yes')
9 RETURN_VALUE
>> 10 LOAD_CONST 0 (None)
13 RETURN_VALUE
在C中是如何实现的Python
优化在CPython 3 in optimize_basic_block()
in Python/compile.c
中实现,截至本文撰写时this line(“删除LOAD_CONST const;条件jump") 进行优化。换句话说,编译器生成这样的操作码:
LOAD_CONST ... (True)
POP_JUMP_IF_FALSE x
LOAD_CONST ... ('yes')
RETURN_VALUE
x LOAD_CONST ... (None)
RETURN_VALUE
所以如果它看到一个LOAD_CONST
后面跟着一个POP_JUMP_IF_FALSE
,并且它可以在编译时证明常量是True
,它会删除LOAD_CONST
并将 POP_JUMP_IF_FALSE
转换为 JUMP_ABSOLUTE
或 NOP
,具体取决于计算的布尔值(此处它将转换为 NOP
):
NOP
NOP
LOAD_CONST ... ('yes')
RETURN_VALUE
x LOAD_CONST ... (None)
RETURN_VALUE
进一步的步骤 (clean_basic_block()
) 将删除 NOP(无操作)并且可能额外的步骤将在 RETURN_VALUE
死后找出该代码并将其删除。