在Python中有时我想做类似(1)的事情
if __debug__ and verbose: print "whatever"
如果 Python 使用 -O 运行,那么我希望整段代码消失,就像我只有 (2) 那样
if __debug__: print "whatever"
甚至 (3)
if __debug__:
if verbose: print foo
然而,这似乎并没有发生(见下文)。有没有一种方法可以让我获得 #3 的运行时效率,同时使用更像 #1 的紧凑代码?
以下是我如何测试我没有获得我想要的高效代码:
#!/usr/bin/python2.7
from dis import dis
import sys
cmds = ["""
def func ():
if __debug__ and 1+1: sys.stdout.write('spam')""", """
def func():
if __debug__: sys.stdout.write('ham')""", """
def func():
__debug__ and sys.stdout.write('eggs')"""]
print "__debug__ is", __debug__, "\n\n\n"
for cmd in cmds:
print "*"*80, "\nSource of {}\n\ncompiles to:".format(cmd)
exec(cmd)
dis(func)
print "\n"*4
运行这个给出
__debug__ is False
********************************************************************************
Source of
def func ():
if __debug__ and 1+1: sys.stdout.write('spam')
compiles to:
3 0 LOAD_GLOBAL 0 (__debug__)
3 POP_JUMP_IF_FALSE 31
6 LOAD_CONST 3 (2)
9 POP_JUMP_IF_FALSE 31
12 LOAD_GLOBAL 1 (sys)
15 LOAD_ATTR 2 (stdout)
18 LOAD_ATTR 3 (write)
21 LOAD_CONST 2 ('spam')
24 CALL_FUNCTION 1
27 POP_TOP
28 JUMP_FORWARD 0 (to 31)
>> 31 LOAD_CONST 0 (None)
34 RETURN_VALUE
********************************************************************************
Source of
def func():
if __debug__: sys.stdout.write('ham')
compiles to:
3 0 LOAD_CONST 0 (None)
3 RETURN_VALUE
********************************************************************************
Source of
def func():
__debug__ and sys.stdout.write('eggs')
compiles to:
3 0 LOAD_GLOBAL 0 (__debug__)
3 JUMP_IF_FALSE_OR_POP 21
6 LOAD_GLOBAL 1 (sys)
9 LOAD_ATTR 2 (stdout)
12 LOAD_ATTR 3 (write)
15 LOAD_CONST 1 ('eggs')
18 CALL_FUNCTION 1
>> 21 POP_TOP
22 LOAD_CONST 0 (None)
25 RETURN_VALUE
不,你不能。 Python 的编译器还不够智能,无法检测在什么情况下可以删除代码块和
if
语句。
否则Python将不得不进行大量的逻辑推理。比较:
if __debug__ or verbose:
与
if __debug__ and verbose:
例如。 Python 必须在编译时检测这两个表达式之间的差异;一个可以优化掉,另一个则不能。
请注意,在其他条件相同的情况下,带有和不带有
if __debug__
语句的代码之间的运行时差异确实很小。一个小的常数值测试和跳跃并不是什么值得大惊小怪的,真的。
您可以从 Python 3.10+ 开始执行此操作。只要你防止 if 条件变得太复杂并首先检查
__debug__
,较新的 python 版本就会按照你想要的方式运行。这是原始代码的修改版本:
from dis import dis
import sys
def func_debug_only(x):
if __debug__:
print('spam')
def func_debug_and_const(x):
if __debug__ and True:
print('spam')
def func_debug_and_var(x):
if __debug__ and x: # -O deletes in this order
print('spam')
def func_debug_and_var2(x):
if x and __debug__: # -O doesn't delete in this order
print('spam')
def func_debug_nested(x):
if __debug__:
if x:
print('spam')
print("__debug__ is", __debug__, "on version", sys.version_info)
for fn in [func_debug_only, func_debug_and_const, func_debug_and_var, func_debug_and_var2, func_debug_nested]:
print('\n', fn.__name__)
dis(fn)
这给你:
__debug__ is False on version sys.version_info(major=3, minor=10, micro=5, releaselevel='final', serial=0)
func_debug_only
6 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
func_debug_and_const
11 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
func_debug_and_var
16 0 LOAD_CONST 0 (None)
2 RETURN_VALUE
func_debug_and_var2
21 0 LOAD_FAST 0 (x)
2 POP_JUMP_IF_FALSE 4 (to 8)
4 LOAD_CONST 0 (None)
6 RETURN_VALUE
>> 8 LOAD_CONST 0 (None)
10 RETURN_VALUE
func_debug_nested
26 0 LOAD_CONST 0 (None)
2 RETURN_VALUE