Python - 輕鬆 trace 程式碼

  • 8562
  • 0

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