在Python中有条件地评估调试语句

时间:2011-10-03 11:39:11

标签: python debugging logging

Python有几种打印“跟踪”输出的方法。 printimport loggingstdout.write可用于打印调试信息,但它们都有一个缺点:即使记录器的阈值太高或流已关闭,Python仍会评估print语句的参数。 (严格评估)这可能会花费字符串格式或更多。

明显的解决方法是将字符串创建代码放入lambda中,并使用我们自己的日志记录函数有条件地调用lambda(这个检查__debug__内置变量,只要python是,就设置为False以-O开始进行优化:

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编译器专门处理。如果python与-O一起运行,那么任何断言语句都会被丢弃而不进行任何评估。您可以利用它来制作另一个有条件评估的日志记录语句:

assert(logging.debug("Working on {0}".format(currentItem)) or True)

使用-O启动Python时,不会评估此行。

短路运营商'和'和'或'甚至可以使用:

__debug__ and logging.debug("Working on {0}".format(currentItem));

但是现在我们最多有28个字符加上输出字符串的代码。

我遇到的问题:是否有任何标准的python语句或函数具有与assert语句相同的条件评估属性?或者,有没有人有这里提出的方法的任何替代方案?

4 个答案:

答案 0 :(得分:3)

如果您的所有调试函数都是一个字符串,为什么不更改它以获取格式字符串和参数:

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__:被移出函数,那么只在加载包含模块时测试一次语句的开销只是一个函数调用。

答案 1 :(得分:2)

我想知道在没有处理程序时,对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 documentation

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)

在这种情况下,如果关闭优化,则永远不会评估消息字符串。

答案 2 :(得分:0)

具有与assert相同的条件行为的任何标准python语句或函数 - >不,据我所知。

请注意,如果阈值太高,logging函数不执行字符串插值(但您仍需支付方法调用和内部的一些检查)。

您可以在代码启动时通过monkeypatching logging.logger扩展Dan D.的建议:

import logging
if __debug__:
    for methname in ('debug', 'info', 'warning', 'error', 'exception'):
         logging.logger.setattr(methname, lambda self, *a, **kwa: None)

然后像往常一样使用日志记录。您甚至可以更改初始测试,以便即使在非优化模式下也可以替换日志记录方法

答案 3 :(得分:0)

您可以使用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)')