我们使用另一个内部团队提供的库。 (现在开始摇摇欲坠的比喻)
from externalTeam import dataCreator
datacreator.createPizza()
datacreator.createBurger()
datacreator.createHotDog()
最近我们发现他们的一种方法在某些情况下需要花费一分钟才能执行。为了调试这个,我不得不进入我们的代码并在每次调用此方法时添加超时。
import time
from externalTeam import dataCreator
start = time.clock()
datacreator.createPizza()
stop = time.clock()
print "It took %s seconds to perform createPizza" % (str(stop-start))
事后来看,那是因为我们在整个地方都在调用createPizza,而我们并不控制createPizza本身(这里的类比开始分解)。我宁愿在一个地方调用createPizza,并且可以在其周围添加一个计时器。我首先想到的是在我自己的包装器类中创建一个包装所有方法。这与DRY相反,只要他们添加另一种方法,我就必须更新我们的库以包装它:
import time
from externalTeam import dataCreator
def createPizza(self):
start = time.clock()
datacreator.createPizza()
stop = time.clock()
print "It took %s seconds to perform createPizza" % (str(stop-start))
def createBurger(self):
start = time.clock()
datacreator.createPizza()
stop = time.clock()
print "It took %s seconds to perform createBurger" % (str(stop-start))
def createHotDog(self):
start = time.clock()
datacreator.createPizza()
stop = time.clock()
print "It took %s seconds to perform createHotDog" % (str(stop-start))
我想要的是一种总是围绕从dataCreator调用的每个函数执行几行代码的方法。必须有一些方法可以通过一个中间类来做到这一点,中间类的方法可以动态定义 - 或者更确切地说是未定义的,对吗?
答案 0 :(得分:5)
如果您正在尝试分析Python代码,则应使用Python的内置profiling libraries而不是尝试手动执行。
答案 1 :(得分:3)
为什么不是一个只调用其参数的包装函数?
def wrapper(func, *args, **kwargs):
... timing logic ...
response = func(*args, **kwargs)
... more timing logic
return response
并称之为:
wrapper(datacreator.createPizza, arg1, arg2, kwarg1=kwarg)
请注意您传递了函数本身,但没有调用它。
答案 2 :(得分:3)
我会创建一个dataCreator
适配器类,它的工作原理如下:
methods2wrap
获取需要包含在调试/计时功能中的dataCreator
方法列表。__getattribute__()
,将1:1映射到dataCreator
方法,将methods2wrap
中的方法包装到计时调试消息中。概念验证代码(示例包装类list
并在其方法append
周围插入调试时间戳。)
import time
class wrapper(list):
def __getattribute__(self, name):
TO_OVERRIDE = ['append']
if name in TO_OVERRIDE:
start = time.clock()
ret = super(list, self).__getattribute__(name)
if name in TO_OVERRIDE:
stop = time.clock()
print "It took %s seconds to perform %s" % (str(stop-start), name)
return ret
profiled_list = wrapper('abc')
print profiled_list
profiled_list.append('d')
print profiled_list
profiled_list.pop()
print profiled_list
当然你可以在这个例子的基础上构建它并使其参数化,这样在初始化时你可以设置要包装的类和应该定时的方法......
编辑:请注意,TO_OVERRIDE
会在每次__getattribute__
来电时重新分配。这是设计的。如果您将它作为类属性,__getattribute__
将递归循环(您应该使用对父__getattribute__
方法的显式调用来检索它,但这可能比简单地重建列表慢从头开始。
HTH
答案 3 :(得分:-1)
以下模板可以提供帮助:
class MeteredClient(Client):
def __init__(self, *args, **kwargs):
super(MeteredClient, self).__init__(*args, **kwargs)
def __getattribute__(self, method_name):
attribute = super(Client, self).__getattribute__(method_name)
if not inspect.ismethod(attribute):
return attribute
metric = TIMINGS.labels(method_name)
def decorator(*args, **kw):
start_time = get_time()
rv = attribute(*args, **kw)
metric.observe(get_time() - start_time)
return rv
return decorator