“有可能吗?”;“扩展”;在Python中使用decorator进行时间监控?
我有一组相互使用的简单函数。例如:“有可能吗?”;“扩展”;在Python中使用decorator进行时间监控?,python,performance,recursion,decorator,python-decorators,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\u 1和func\u 2和func\u 2的“根”函数,依次使用func\u a和func\u b。当我调用fu
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\u 1
和func\u 2
和func\u 2
的“根”函数,依次使用func\u a
和func\u b
。当我调用func
时,得到的结果是z
现在我想用一个修饰符“修改”或“扩展”我的函数,这样最终(由于func
),我不仅得到z
,而且得到一个对象,它向我显示执行这个函数需要多少时间,函数使用了哪些函数,以及执行这些“子函数”需要多长时间以及哪些“子功能”使用了哪些“子功能”以及执行它们需要多长时间。为了使其更简单,我举了一个我期望的“额外”结果的例子:
在上面的示例中,“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
但接下来我将继续进行进一步的实现。我找不到解决办法,甚至不确定这是否可能
我的想法是使用一个装饰器来扩展传递给每个函数的参数数量。每个函数都会得到一个空列表,其中包含迄今为止使用的所有子函数,并将其自身附加到此列表。您最好按照建议使用真实的探查器 不过,它可以通过decorator类完成。您将能够使用在所有装饰器实例之间共享的堆栈跟踪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": []
}
]
}
]
}
decorator环绕函数,它看到它接收(参数)和发回的内容(return
values),但不看到它调用的内容。要使用decorator实现这一点,您必须包装所有涉及在不同函数调用中共享状态的函数。或者,使用现有的剖析器。不是说你的问题是不合法的,但你几乎是在重新发明轮子。程序包中的@profile
装饰程序正是这样做的。有关用法示例和更多详细信息,请参见。@jornsharpe,我知道。但是,如果每个函数都将同一个对象从其参数传递到其所有子函数,并且每个子函数都向该对象写入了某些内容,那么,在decorator中,在执行包装函数后,我们将获得子函数写入的信息。那么,您希望函数的一个参数实际上是用于其decorator的吗?这看起来很混乱,这意味着修饰过的函数将与未修饰过的函数具有不同的调用签名。在你开始运行自己的代码之前,先看看现有的代码分析选项。如果是这样的话,这似乎完全与Python背道而驰。最好直接将参数传递给装饰师。这往往是最容易学习的类装饰,类似于sebdestol的答案。我认为这是倒退。当您将参数传递给decorator类时,函数不是传递给call,参数不是传递给init吗?当decorator没有参数时不是,\uuuu init\uuuuu
在函数f被修饰时被调用,并且每次f被调用时都会调用\uuu call\uuuuuu
。如果函数调用,您可能会使用一些try/except
逻辑fail@acushner你说得对。我想还有很多其他的方法会失败。但这并不是真正的问题。所以我宁愿保持代码简单。
{
"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": []
}
]
}
]
}