Decorator+Reflection可以选择性地创建表示单个函数调用链的对象树吗



给定:

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时的内容不同。

最新更新