给定:
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应用decorator Track,每次调用f,h、i和j:
- 实例化一个包含函数名"f"、"h"、"i"以及arg和返回值的"Call"对象
- 使用反射来搜索最近的类似修饰的函数,该函数(直接或间接(调用它,即它会将调用传递给g((,因为它不是@Tracked
- 将上面的"Call"对象追加到调用方的"Call"对象上的"children"列表中,如果找不到合适的调用方,则追加到全局列表G中
对于代码:
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=...)
我被困在反射/遍历运行时堆栈帧部分。
谢谢!
从形式上讲,结果是这样的
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)
但有可能同意其他回溯在这里会有所帮助,尽管回溯没有显示调用参数
实例化一个包含函数名"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"列表中"Call"对象,或者如果没有合适的调用者则指向全局列表G找到
请记住,当您使用参数和返回值存储调用时,它们不会被垃圾收集,并且您的内存会增长。此外,如果您存储可变对象(例如,列表(,您稍后在Call
中看到的内容可能与创建Call
时的内容不同。