在我们执行拥有复杂函数调用关系的一段python程序时,我们希望能够清楚的知道他们之间的调用关系以及在调用过程中传入的参数信息和返回值,这些信息对于我们分析程序的行为和bug会很有帮助。
我希望能实现一个装饰器,用于追踪函数的调用关系,下面是这个装饰器的简单实现
import sys
from functools import wraps
class FuncTrace:
indent = 0
def __init__(self, stream=sys.stdout, indent_step=2, is_show_res=True):
self.steam = stream
self.indent_step = indent_step
self.is_show_res = is_show_res
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
curr_indent = ' '*FuncTrace.indent
# 参数信息
arg_lst = [repr(item) for item in args]
kwarg_lst = ['{key}={value}'.format(key=key, value=value) for key, value in kwargs.items()]
arg_str = ','.join(arg_lst + kwarg_lst)
msg = "{curr_indent}{func_name}({arg})\n".format(curr_indent=curr_indent, func_name=func.__name__, arg=arg_str)
self.steam.write(msg)
# 下一次调用时, 增加缩进
FuncTrace.indent += self.indent_step
result = func(*args, **kwargs)
FuncTrace.indent -= self.indent_step # 调用结束后减少缩进
if self.is_show_res:
msg = "{indent}return {result}\n".format(indent=curr_indent, result=result)
self.steam.write(msg)
return result
return wrapper
调用函数时的参数信息十分关键,因此将args和kwargs拼接在一起输出,不足之处在于,如果参数里的数据内容较多,比如一个参数是列表,列表里有超过100个元素,那么现有的这种写法会将这100个元素都输出,很影响输出结果的展示,这是一个可以优化的点。
通过在输出信息中增加缩进,来表现函数之间的调用关系,在函数执行前增加缩进,函数执行结束后减少缩进。
默认输出流是sys.stdout, 你也可以将它设置为文件或者log, 下面是这个装饰器的用法展示
def func_c(number):
if number % 3 == 0:
return number**2
return number*2
@FuncTrace()
def func_b(number):
if number % 2 == 0:
return number*2
else:
return func_c(number)
@FuncTrace()
def func_a(lst):
result = []
for i in lst:
result.append(func_b(i))
return result
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
func_a(lst)
程序输出结果
func_a([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
func_b(1)
func_c(1)
return 2
return 2
func_b(2)
return 4
func_b(3)
func_c(3)
return 9
return 9
func_b(4)
return 8
func_b(5)
func_c(5)
return 10
return 10
func_b(6)
return 12
func_b(7)
func_c(7)
return 14
return 14
func_b(8)
return 16
func_b(9)
func_c(9)
return 81
return 81
func_b(10)
return 20
return [2, 4, 9, 8, 10, 12, 14, 16, 81, 20]
QQ交流群: 211426309