这是我的调试装饰器。
import sys
import time
level = 0
def debug(f):
def decorated(*args, **kwargs):
global level
sys.stderr.write("[debug] %s%s(%s)\n"
% (' ' * level,
f.__name__, ", ".join(
[str(a) for a in args]
+ ["%s=%s" % (k, v) for (k, v) in kwargs.items()])))
level += 1
t0 = time.time()
res = f(*args, **kwargs)
t1 = time.time()
level -= 1
sys.stderr.write("[debug] %s= %r (%.9f s)\n"
% (' ' * level, res, t1 - t0))
return res
return decorated
这是对 Phil 的改进,因为:
- 它使用标准错误,因此在标准输出上保留有意义的输出,
- 它提高了递归调用的可见性
- 它测量被调试函数的持续时间(没有要求,但也不是很大的障碍)
我使用f.__name__ 而不是f.func_name,因为前者在python 3.8 中不再适用。
我从 John Montgomery 那里得到了不要忘记治疗 kwargs 的提示。
我没有保留 DEBUG 全局变量,不是因为它的性质,而是因为我认为在我希望调试/清理的位置放置/删除带有 @debug 的行更简单。我不会提交它们,因为我将准时手动调试与日志记录区分开来。
小例子
@debug
def f(a, b):
return a + b
@debug
def g(a, b, c):
return f(a, b) * c
class C:
@debug
def m1(self, a, b):
return a / b
@classmethod
@debug
def m2(c, a, b):
return b - a
@staticmethod
@debug
def m3(a, b):
return a * b
if __name__ == '__main__':
f(1, b=2)
g(1, 2, 3)
C().m1(3, 4)
C.m2(5, 6)
C.m3(7, 8)
输出
$./test.py >/dev/null
[debug] f(1, b=2)
[debug] = 3 (0.000001431 s)
[debug] g(1, 2, 3)
[debug] f(1, 2)
[debug] = 3 (0.000000477 s)
[debug] = 9 (0.000024557 s)
[debug] m1(<__main__.C object at 0x7f10a6465d90>, 3, 4)
[debug] = 0.75 (0.000000715 s)
[debug] m2(<class '__main__.C'>, 5, 6)
[debug] = 1 (0.000000477 s)
[debug] m3(7, 8)
[debug] = 56 (0.000000477 s)
我知道真正的调试器是一个应该知道的工具,但应该考虑到在编辑器中添加一个小装饰器的简单性。与任何工具一样,正确使用由上下文定义。