是否有可能做到"扩展"使用装饰器在Python中进行时间监控?

时间:2014-09-03 15:34:41

标签: python performance recursion decorator python-decorators

我有一组相互使用的简单函数。例如:

def func(x)
    y = func_1(x)
    z = func_2(y)
    return z

def func_1(x):
    return x + 1

def func_2(x)
    a = func_a(x)
    b = func_b(y)
    return b

正如您所看到的,func是使用func_1func_2的“根”功能,而func_2依次使用func_afunc_b。当我致电func时,我得到z作为结果。

现在我想用装饰器“修改”或“扩展”我的函数,以便最终(作为func的结果)我不仅得到z而且得到一个对象向我展示了执行此函数需要多少以及函数使用了哪些函数以及执行这些“子函数”需要多长时间以及什么“子子函数”已被使用“子功能”以及执行它们需要多长时间。为了简单起见,我举一个例子来说明我期望的“附加”结果:

{
    'fname' : 'func',
    'etime' : 12.000,
    'subs'  : [
        {
        'fname' : 'func_1',
        'etime' : 2.000,
        'subs'  : []
        },
        {
        'fname' : 'func_2',
        'etime' : 10,
        'subs'  : [
            {
            'fname' : 'func_a',
            'etime' : 6,
            'subs' : []
            },
            {
            'fname' : 'func_b',
            'etime' : 4
            'subs' : []
            }
        ]
        }
    ]
}

在上面的示例中,“fname”表示函数的名称,“etime”表示执行时间(执行此函数需要多长时间),“subs”是由子函数使用的子函数列表。考虑功能。对于每个子功能,我们有相同的键(“fname”,“etime”,“subs”)。所以,它是一个“递归”结构。如果函数没有使用任何函数,则“subs”映射到空列表。

我已经开始使用以下装饰器:

def decorate(func):

    def wrapper(*args, **kw):

        d = {}
        d['fname'] = func.__name__
        t0 = time.time()

        out = func(*args, **kw)

        d['etime'] = time.time() - t0

        d['subs'] = ?

        ?.append(d)

    return wrapper

然后我与进一步的实现堆叠。我找不到解决方案,甚至不确定是否有可能。

我的想法是我使用装饰器来扩展传递给每个函数的参数数量。每个函数都会获得一个包含目前使用的所有子函数的空列表,并将其自身附加到此列表中。

1 个答案:

答案 0 :(得分:3)

如果建议使用真实的探查器,你会更好。

仍然可以使用装饰器类来完成。您将能够使用在所有装饰器实例之间共享的堆栈来跟踪子列表。

class profile(object):
    #class variable used as a stack of subs list
    stack = [[]] 

    def __init__(self, f):
        self.f = f

    def __call__(self, *args, **kw):
        func = dict(fname = self.f.__name__)

        #append the current function in the latest pushed subs list
        profile.stack[-1].append(func)

        #push a new subs list in the stack
        profile.stack.append([]) 

        #execution time of the actual call
        t0 = time.time()
        out = self.f(*args, **kw)
        func['etime'] = time.time() - t0

        #pull the subs list from the stack
        func['subs'] = profile.stack.pop()

        return out

    @classmethod
    def show(cls):
        import json #useful to prettify the ouput
        for func in cls.stack[0]:
            print json.dumps(func, sort_keys=True, indent=4)

您必须使用@profile修饰要在个人资料中显示的所有功能。 请注意,在实际情况中,您可能希望在装饰函数失败时处理异常。

<强>输出继电器:

profile.show()显示所有被调用的“root”函数列表以及所有内部调用。

{
    "etime": 4.5, 
    "fname": "func", 
    "subs": [
        {
            "etime": 1.0, 
            "fname": "func_1", 
            "subs": []
        }, 
        {
            "etime": 3.5, 
            "fname": "func_2", 
            "subs": [
                {
                    "etime": 1.5, 
                    "fname": "func_a", 
                    "subs": []
                }, 
                {
                    "etime": 2.0, 
                    "fname": "func_b", 
                    "subs": []
                }
            ]
        }
    ]
}