Python - 輕鬆 trace 程式碼
最近艾小克接手一個既有的系統,由於系統比較複雜,在 PyScripter 中使用 debugger,一步一步的 tracing 程式碼就顯得比較沒有效率。
上網 Google 一下,發「Easy tracing of nested function calls in Python」文章使用 python decorator 的技巧。
在既有的程式碼中加入屬性,就可以記錄 function call 的進 (呼叫參數) 出 (回傳值)。
可以簡單複製以下程式碼,另存為 TraceCalls.py:
import sys from functools import wraps class TraceCalls(object): """ Use as a decorator on functions that should be traced. Several functions can be decorated - they will all be indented according to their call depth. """ def __init__(self, stream=sys.stdout, indent_step=2, show_ret=False): self.stream = stream self.indent_step = indent_step self.show_ret = show_ret # This is a class attribute since we want to share the indentation # level between different traced functions, in case they call # each other. TraceCalls.cur_indent = 0 def __call__(self, fn): @wraps(fn) def wrapper(*args, **kwargs): indent = ' ' * TraceCalls.cur_indent argstr = ', '.join( [repr(a) for a in args] + ["%s=%s" % (a, repr(b)) for a, b in kwargs.items()]) self.stream.write('%s%s(%s)\n' % (indent, fn.__name__, argstr)) TraceCalls.cur_indent += self.indent_step ret = fn(*args, **kwargs) TraceCalls.cur_indent -= self.indent_step if self.show_ret: self.stream.write('%s--> %s\n' % (indent, ret)) return ret return wrapper
另外,我們寫一段簡單的程式來進行測試,例如 fibonacci 數列:
#!/usr/bin/python # -*- coding: utf-8 -*- from TraceCalls import TraceCalls @TraceCalls(indent_step=4, show_ret=True) def fibonacci(x): if x in (0, 1): return x return fibonacci(x - 1) + fibonacci(x - 2) # main print 'f(%d): %d' % (4, fibonacci(4))
其中 from TraceCalls import TraceCalls 是進行引用…
@TraceCalls(indent_step=4, show_ret=True) 則是 decorator 的宣告方式,而預設值是 @TraceCalls()…
indent_step 屬性:定義 indent 空格的 size
show_ret 屬性:定義是否列印回傳值,預設是不開啟
以下是印出整個 fibonacci(4) 遞迴呼叫的過程;
fibonacci(4) fibonacci(3) fibonacci(2) fibonacci(1) --> 1 fibonacci(0) --> 0 --> 1 fibonacci(1) --> 1 --> 2 fibonacci(2) fibonacci(1) --> 1 fibonacci(0) --> 0 --> 1 --> 3 f(4): 3
有了 TraceCalls,就可以快速檢視整個程式呼叫的歷程囉…