Python中相当于'#define func()'的写法是什么?或者如何在Python中注释掉函数调用?

10 投票
6 回答
15199 浏览
提问于 2025-04-15 17:39

我的Python代码里夹杂了很多用于调试、性能分析、追踪等的函数调用。

import logging

logging.root.setLevel(logging.DEBUG)
logging.debug('hello')
j = 0
for i in range(10):
    j += i
    logging.debug('i %d j %d' % (i,j))
print(j)
logging.debug('bye')

我想把这些消耗资源的函数从代码中去掉,类似于C语言里的那种做法。

#define logging.debug(val)

是的,我知道可以用日志模块的日志级别机制来屏蔽低于设定日志级别的日志输出。但是,我想要的是一种通用的方法,让Python解释器跳过那些运行起来耗时的函数(即使它们实际上做的事情不多)。

一个想法是把我想要注释掉的函数重新定义成空函数:

def lazy(*args): pass
logging.debug = lazy

不过,上面的想法还是会调用一个函数,可能会引发很多其他问题。

6 个回答

0

使用一个模块范围的变量?

from config_module import debug_flag

然后用这个“变量”来控制访问日志记录功能。你可以自己创建一个 logging 模块,利用 debug_flag 来控制日志记录的功能。

2

虽然我觉得这个问题很清楚也很合理(尽管有很多回答表示不同意见),但简单来说就是“Python对此没有支持”。

除了预处理器的建议,唯一可能的解决办法就是用一些字节码黑客技术。我甚至不想去想这个在高级接口上应该怎么实现,但从低层次上讲,你可以想象检查代码对象中的特定指令序列,然后重写它们以去掉某些部分。

比如,看看下面这两个函数:

>>> def func():
...    if debug:  # analogous to if __debug__:
...       foo
>>> dis.dis(func)
  2           0 LOAD_GLOBAL              0 (debug)
              3 JUMP_IF_FALSE            8 (to 14)
              6 POP_TOP

  3           7 LOAD_GLOBAL              1 (foo)
             10 POP_TOP
             11 JUMP_FORWARD             1 (to 15)
        >>   14 POP_TOP
        >>   15 LOAD_CONST               0 (None)
             18 RETURN_VALUE

在这里,你可以扫描debugLOAD_GLOBAL指令,然后去掉它和直到JUMP_IF_FALSE目标之间的所有内容。

这个是更传统的C风格的debug()函数,可以通过预处理器轻松去掉:

>>> def func2():
...    debug('bar', baz)
>>> dis.dis(func2)
  2           0 LOAD_GLOBAL              0 (debug)
              3 LOAD_CONST               1 ('bar')
              6 LOAD_GLOBAL              1 (baz)
              9 CALL_FUNCTION            2
             12 POP_TOP
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

在这里,你会寻找debugLOAD_GLOBAL指令,并去掉直到对应的CALL_FUNCTION的所有内容。

当然,以上描述的操作比实际需要的要简单得多,尤其是对于那些最简单的使用模式,但我觉得这是可行的。如果还没有人做过,这会是一个有趣的项目。

17

Python没有预处理器,虽然你可以通过外部的预处理器来实现类似的效果,比如用sed "/logging.debug/d"这个命令可以把所有的调试日志命令去掉。不过,这样做并不是很优雅,你可能还需要一个构建系统来处理所有模块,通过预处理器生成新的目录结构,里面放的是处理过的.py文件,然后再运行主脚本。

另外,如果你把所有的调试语句放在if __debug__:这个块里,当你用-O(优化)标志运行Python时,这些调试语句就会被优化掉。

顺便提一下,我用dis模块检查了代码,确保它确实被优化掉了。我发现这两个

if __debug__: doStuff()

if 0: doStuff()

都被优化了,但

if False: doStuff()

没有被优化。这是因为False是一个普通的Python对象,实际上你可以这样做:

>>> False = True
>>> if False: print "Illogical, captain"
Illogical, captain

我觉得这在语言上是个缺陷,希望在Python 3中能得到修复。

编辑:

在Python 3中这个问题已经修复:给True或False赋值现在会出现语法错误< a href="http://docs.python.org/dev/3.0/library/constants.html" rel="noreferrer">。由于True和False在Python 3中是常量,这意味着if False: doStuff()现在会被优化:

>>> def f():
...     if False: print( "illogical")
... 
>>> dis.dis(f)
  2           0 LOAD_CONST               0 (None) 
              3 RETURN_VALUE         

撰写回答