【问题标题】:Is it possible to do "extended" time monitoring in Python using a decorator?是否可以使用装饰器在 Python 中进行“扩展”时间监控?
【发布时间】:2014-10-28 04:38:05
【问题描述】:

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

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_2func_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

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

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

【问题讨论】:

  • 装饰器环绕函数,它查看接收到的内容(参数)并返回(return 值),但不是它调用的内容。要使用装饰器执行此操作,您必须包装所有涉及在不同函数调用之间共享状态的函数。或者,使用现有的分析器。
  • 并不是说您的问题不合法,但您在这里几乎是在重新发明轮子。 profilestats 包中的 @profile 装饰器正是这样做的。另请参阅 this answer 以获取使用示例和更多详细信息。
  • @jonrsharpe,我知道。但是,如果每个函数将同一个对象从它的参数传递给它的所有子函数,并且每个子函数都向这个对象写入一些东西,那么在装饰器中,在执行包装函数之后,我们会得到由子函数写入的信息。
  • 所以你想让函数的参数之一实际上是它的装饰器?这看起来很混乱,这意味着修饰函数将具有与未修饰版本不同的调用签名。在您提交自己的代码之前,请查看用于分析代码的现有选项。
  • 这似乎与 Python 完全相反,如果是这样的话。你最好直接将参数传递给装饰器。这往往是使用类装饰器最容易学习的方法,类似于 sebdestol 的答案。

标签: python performance recursion decorator python-decorators


【解决方案1】:

您最好按照建议使用真正的分析器。

不过,它可以通过装饰器类来完成。您将能够使用在所有装饰器实例之间共享的堆栈来跟踪 subs 列表。

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() 显示了所有被调用的“根”函数的列表及其所有内部调用。

{
    "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": []
                }
            ]
        }
    ]
}

【讨论】:

  • 我认为这是倒退。当您将参数传递给装饰器类时,函数不是传递给 call 而参数传递给 init 吗?
  • 不是当装饰器没有参数时,__init__ 在函数 f 被装饰时调用,__call__ 在每次 f 被调用时调用。
  • 你可能会使用一些try/except逻辑以防函数调用失败
  • @acushner,你是对的。我想还有很多其他方法可能会失败。然而,这并不是真正的问题。所以我宁愿保持这段代码简单。
猜你喜欢
  • 2021-12-01
  • 2020-08-20
  • 1970-01-01
  • 2011-12-13
  • 2011-07-16
  • 1970-01-01
  • 2019-05-17
  • 2019-10-17
  • 1970-01-01
相关资源
最近更新 更多