Python中的条件调试语句
在Python中,有几种方法可以打印“跟踪”输出。你可以使用 print
、import logging
和 stdout.write
来打印调试信息,但它们都有一个缺点:即使日志记录器的阈值设置得太高,或者输出流已经关闭,Python仍然会计算打印语句的参数。(严格求值)这可能会浪费时间去处理字符串格式等。
一个明显的解决办法是把生成字符串的代码放到一个lambda表达式里,然后用我们自己的日志记录函数来有条件地调用这个lambda(这个函数会检查 __debug__
这个内置变量,当你用 -O
启动Python进行优化时,它会被设置为False):
def debug(f):
if __debug__:
print f()
#stdout.write(f())
#logging.debug(f())
for currentItem in allItems:
debug(lambda:"Working on {0}".format(currentItem))
这样做的好处是在发布版本中不会调用 str(currentItem)
和 string.format
,缺点是每次写日志时都得输入 lambda:
。
Python的 assert
语句在Python编译器中是特殊处理的。如果用 -O
启动Python,那么任何assert语句都会被丢弃,不会被计算。你可以利用这一点来创建另一个有条件评估的日志记录语句:
assert(logging.debug("Working on {0}".format(currentItem)) or True)
当用 -O
启动Python时,这行代码不会被计算。
甚至可以使用短路运算符 'and' 和 'or':
__debug__ and logging.debug("Working on {0}".format(currentItem));
不过现在我们得写28个字符,加上输出字符串的代码。
我想问的是:有没有什么标准的Python语句或函数,具有和 assert
语句一样的条件评估特性?或者,有没有人有其他替代这里提到的方法?
4 个回答
你可以使用 eval 这个方法:
import inspect
def debug(source):
if __debug__:
callers_locals = inspect.currentframe().f_back.f_locals
print eval(source, globals(), callers_locals)
for currentItem in ('a', 'b'):
debug('"Working on {0}".format(currentItem)')
如果你所有的调试函数只需要一个字符串,那为什么不把它改成接收一个格式化字符串和一些参数呢:
debug(lambda:"Working on {0}".format(currentItem))
就变成了
debug("Working on {0}", currentItem)
而且
if __debug__:
def debug(format, *values):
print format.format(*values)
else:
def debug(format, *values): pass
这样做有了你第一种选择的所有优点,而且不需要使用一个额外的lambda函数。如果把if __debug__:
放到函数外面,这样在加载模块时只会测试一次,那么这个语句的开销就只是一条函数调用。
我想知道,当没有处理程序时,调用 logging.debug
会对性能产生多大影响。
不过,if __debug__:
这个语句只会被评估一次,即使是在函数的内部。
$ python -O
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> import logging
>>> def debug(*a, **kw):
... if __debug__:
... logging.debug(*a, **kw)
...
>>> dis.dis(debug)
2 0 LOAD_CONST 0 (None)
3 RETURN_VALUE
>>>
而且,记录器可以使用字符串格式化操作符来帮你格式化消息。这里有一个稍微修改过的例子,来自于 logging.debug 文档。
FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = { 'clientip' : '192.168.0.1', 'user' : 'fbloggs' }
debug('Protocol problem: %s', 'connection reset', extra=d)
在这种情况下,如果优化没有开启,消息字符串是不会被计算的。