假设:
G = []
@Track
def f(x):
a = g(x)
b = h(x + 2)
return a + b
def g(x)
for n in range(2):
i(x + n)
@Track
def h(x):
return j(x) + 9
@Track
def i(x):
return x + 10
@Track
def j(x):
return 0
是否可以写&应用装饰器跟踪f,h,i,j将在每次调用f,h,i和j时应用:
代码:
f(3)
j(3)
这应该创建以下连接对象树:
G
-- Call(name='f',args=..., return=...)
-- Call(name='i',args=..., return=...)
-- Call(name='i',args=..., return=...)
-- Call(name='i',args=..., return=...)
-- Call(name='h',args=..., return=...)
-- Call(name='j', args=..., return=...)
-- Call(name='j', args=..., return=...)
我被困在反射/遍历运行时堆栈帧部分。
谢谢!
答案 0 :(得分:0)
实例化一个包含函数名称'f','h','i'的'Call'对象, 和arg-和返回值
global_list_of_calls = defaultdict(list)
def Track(func):
def _tracked(*args, **kwargs):
res = func(*args, **kwargs)
c = Call(func.__name__, args, kwargs, res)
# do something with c, store it in a global container
global_list_of_calls.append(c)
return res
return _tracked
使用反射搜索最近的类似装饰的功能 称它(直接或间接),即它将通过呼叫 到g(),因为那不是@Tracked。
traceback
将在此处提供帮助
将上述“Call”对象附加到调用者的“children”列表中 “呼叫”对象,如果没有合适的呼叫者,则转到全局列表G. 结果
请记住,当您使用参数存储调用并返回值时,它们将不会被垃圾回收并且您的内存将会增长。此外,如果您存储可变对象(例如,列表),您在Call
中稍后看到的内容可能与创建Call
时的内容不同
答案 1 :(得分:0)
正式地说,结果是这样的
stack=[]
shift=0
def Track(func):
def wrapper(*args, **kwargs):
global shift
stack.append([])
el=stack[-1]
el.append('%s -- call(name=%s,args=%s,kwargs=%s)' % (' '*shift,func.__name__,args, kwargs))
shift+=1
res = func(*args, **kwargs)
shift-=1
el[0]+='return=%s)' % res
return res
return wrapper
用于打印:
for i in stack: print i[0]
显示
-- call(name=f,args=(3,),kwargs={})return=10)
-- call(name=i,args=(3,),kwargs={})return=13)
-- call(name=i,args=(4,),kwargs={})return=14)
-- call(name=h,args=(5,),kwargs={})return=9)
-- call(name=j,args=(5,),kwargs={})return=0)
-- call(name=j,args=(3,),kwargs={})return=0)
但是有可能同意其他追溯在这里会有所帮助, 虽然回溯没有显示调用参数