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,就可以快速檢視整個程式呼叫的歷程囉…