Decorator + Reflection能否有选择地创建表示单个函数调用链的对象树?

时间:2013-03-19 05:52:38

标签: python

假设:

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时应用:

  • 实例化一个'Call'对象,其中包含函数名'f','h','i'以及arg-和return-values
  • 使用反射搜索最近的类似装饰的函数(直接或间接),即它将调用g(),因为它不是@Tracked。
  • 将上述“呼叫”对象附加到呼叫者“呼叫”对象上的“子”列表中,如果找不到合适的呼叫者,则附加到全局列表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=...)

我被困在反射/遍历运行时堆栈帧部分。

谢谢!

2 个答案:

答案 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)

但是有可能同意其他追溯在这里会有所帮助, 虽然回溯没有显示调用参数