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

Python Decorator+Reflection能否有选择地创建表示单个函数调用链的对象树?,python,Python,鉴于: 是否可以为f,h,i,j编写并应用一个decorator跟踪,它将在每次调用f,h,i和j时: 实例化一个“Call”对象,该对象包含函数名“f”、“h”、“i”以及arg和返回值 使用反射搜索直接或间接调用它的最近的类似修饰函数,即它会将调用传递给g,因为g没有@Tracked。 将上述“Call”对象附加到调用方“Call”对象上的“children”列表中,如果未找到合适的调用方,则附加到全局列表G中 守则: G = [] @Track def f(x): a = g(x)

鉴于:

是否可以为f,h,i,j编写并应用一个decorator跟踪,它将在每次调用f,h,i和j时:

实例化一个“Call”对象,该对象包含函数名“f”、“h”、“i”以及arg和返回值 使用反射搜索直接或间接调用它的最近的类似修饰函数,即它会将调用传递给g,因为g没有@Tracked。 将上述“Call”对象附加到调用方“Call”对象上的“children”列表中,如果未找到合适的调用方,则附加到全局列表G中 守则:

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(3)
j(3)
我被困在反射/遍历运行时堆栈帧部分

谢谢

实例化包含函数名“f”、“h”、“i”的“Call”对象, 以及arg和返回值

使用反射搜索最近的相似修饰函数 直接或间接呼叫它,即它将传递呼叫 对g来说,因为这不是@Tracked

在这里会有帮助的

将上述“Call”对象附加到调用方的“children”列表中 “Call”对象,如果没有合适的调用者,则调用全局列表G 发现


请记住,当您存储带有参数和返回值的调用时,它们不会被垃圾收集,内存也会增加。此外,如果存储可变对象(例如,列出稍后在调用中看到的内容)可能与正式创建调用时看到的不一样,结果如下

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
印刷品:

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
展示

但有可能同意另一种说法,追溯在这里会很有帮助,
虽然回溯没有显示调用参数

但回溯将有助于遍历,我确实让基本轨迹装饰器包装它的函数,捕获参数和返回值,并将它们填充到调用对象中。被困在如何找到合适的来电者。感谢回溯链接。它似乎主要是为打印输出而设计的,而不是我想要的那种用法。查看extract_tb和extract_stack函数这看起来很有希望,我没有想到要维护自己的堆栈。我将更深入地探讨如何使用这种方法。谢谢
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)