关闭调试打印

时间:2012-08-31 22:28:38

标签: python

有时我会在功能周围散布很多打印件来打印调试输出。 为了切换这个调试输出,我提出了这个:

def f(debug=False): 
    print = __builtins__.print if debug else lambda *p: None

或者,如果我需要打印除调试消息之外的内容,我会为调试消息创建dprint函数。

问题是,当debug=False时,这个print语句会大大减慢代码的速度,因为lambda *p: None仍然被调用,并且已知函数调用很慢。

所以,我的问题是:有没有更好的方法来有效地禁用所有这些调试打印,以免影响代码性能?


所有答案都与我未使用logging模块有关。这是一个值得注意的问题,但是没有回答这个问题如何避免大大减慢代码的函数调用 - 在我的情况下是25次(如果可能的话)(例如通过修改函数)代码对象通过print语句或其他方式来消除所有行))。这些答案建议将print替换为logging.debug,这应该更慢。而这个问题是完全摆脱那些函数调用。

我尝试使用logging代替lambda *p: None,毫不奇怪,代码变得更慢。


也许有人希望看到那些打印导致速度减慢的代码:http://ideone.com/n5PGu

我对logging模块没有任何反对意见。我认为在没有hacks的情况下始终坚持强大的解决方案是一种很好的做法。但我认为如果我在20行一次性代码片段中使用那些 hacks ,就没有任何罪行。


不作为限制,但作为建议,也许可以从函数源代码中删除一些行(例如从print开始)并重新编译它?我在下面的答案中列出了这种方法。虽然我想看一些关于该解决方案的评论,但我欢迎其他方法来解决这个问题。

5 个答案:

答案 0 :(得分:5)

您应该使用logging模块。见http://docs.python.org/library/logging.html

然后,您可以根据需要设置日志级别,并创建多个记录器对象,记录不同的主题。

import logging
#set your log level
logging.basicConfig(level=logging.DEBUG)
logging.debug('This is a log message')

在您的情况下:您可以简单地用日志语句替换print语句,例如:

import logging
print = __builtins__.print if debug else logging.debug

现在,只有将日志记录级别设置为debug

,该函数才会打印任何内容
logging.basicConfig(level=logging.DEBUG)

但是作为一个优点,您可以在顶部使用所有其他日志记录功能! logging.error('error!')

答案 1 :(得分:2)

Ned Batchelder the comment写道:

  

我怀疑减速是在计算参数时   你的调试功能。你应该寻找避免这些的方法   计算。预处理Python只是一种分心。

他是正确的,因为减速实际上是由使用format方法格式化字符串引起的,无论是否记录结果字符串都会发生。

因此,如果不进行日志记录,则应延迟并取消字符串格式。这可以通过以下方式重构dprint函数或使用log.debug来实现:

log.debug('formatted message: %s', interpolated_value)

如果不记录消息,它将不会被格式化,与print不同,它始终是格式化的,无论是否记录或丢弃它。

log.debug推迟格式的解决方案为 Martijn Pieters here提供了解决方案。

答案 2 :(得分:0)

作为一个黑客,是的,这是有效的。 (并且没有机会在那些lambda no-ops是你的应用程序的瓶颈。)

但是,您确实应该使用logging模块正确进行日志记录。

有关如何完成此操作的基本示例,请参阅http://docs.python.org/howto/logging.html#logging-basic-tutorial

答案 3 :(得分:0)

您肯定需要使用Python的logging模块,它非常实用,您可以更改应用程序的日志级别。例如:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.debug('Test.')
DEBUG:root:Test.

答案 4 :(得分:0)

另一种解决方案可能是动态编辑f的代码并删除所有drpint次调用。但是这个解决方案非常unrecommended to be used

  

你是对的,你不应该诉诸于此,有这么多   它可能出错的方式。首先,Python不是为其设计的语言   源级转换,很难把它写成变压器   例如comment_1没有无偿破坏有效代码。第二,   这种黑客会在各种情况下破裂 - 例如,   定义方法时,定义嵌套函数时,在使用时   Cython,当inspect.getsource因任何原因失败时。 Python是   足够动态,你真的不需要这种黑客   定制其行为。

以下是这种方法的代码,适合那些喜欢熟悉它的人:

from __future__ import print_function

DEBUG = False

def dprint(*args,**kwargs):
    '''Debug print'''
    print(*args,**kwargs)

_blocked = False
def nodebug(name='dprint'):
    '''Decorator to remove all functions with name 'name' being a separate expressions'''
    def helper(f):      
        global _blocked
        if _blocked:
            return f

        import inspect, ast, sys

        source = inspect.getsource(f)        
        a = ast.parse(source) #get ast tree of f

        class Transformer(ast.NodeTransformer):
            '''Will delete all expressions containing 'name' functions at the top level'''
            def visit_Expr(self, node): #visit all expressions
                try:
                    if node.value.func.id == name: #if expression consists of function with name a
                        return None #delete it
                except(ValueError):
                    pass
                return node #return node unchanged
        transformer = Transformer()
        a_new = transformer.visit(a)
        f_new_compiled = compile(a_new,'<string>','exec')

        env = sys.modules[f.__module__].__dict__
        _blocked = True
        try:
            exec(f_new_compiled,env)
        finally:
            _blocked = False
        return env[f.__name__]         
    return helper


@nodebug('dprint')        
def f():
    dprint('f() started')
    print('Important output')
    dprint('f() ended')
    print('Important output2')


f()

更多信息:Replacing parts of the function code on-the-fly