有时我会在功能周围散布很多打印件来打印调试输出。 为了切换这个调试输出,我提出了这个:
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
开始)并重新编译它?我在下面的答案中列出了这种方法。虽然我想看一些关于该解决方案的评论,但我欢迎其他方法来解决这个问题。
答案 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()