Python:将所有函数包装到库中
我们使用另一个内部团队提供的库。(不可靠的类比现在开始) 最近,我们发现他们的一个方法在某些情况下需要一分钟才能执行。为了调试这个,我必须进入我们的代码,并在这个方法的每个调用周围添加超时Python:将所有函数包装到库中,python,wrapper,Python,Wrapper,我们使用另一个内部团队提供的库。(不可靠的类比现在开始) 最近,我们发现他们的一个方法在某些情况下需要一分钟才能执行。为了调试这个,我必须进入我们的代码,并在这个方法的每个调用周围添加超时 import time from externalTeam import dataCreator start = time.clock() datacreator.createPizza() stop = time.clock() print "It took %s seconds to perform cr
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调用的每个函数执行几行代码的方法。必须有某种方法通过一个中间类来实现这一点,这个中间类的方法可以动态定义,或者说是不定义,对吗?如果您试图评测Python代码,那么应该使用Python的内置函数,而不是手动执行 为什么不使用一个只调用其参数的包装函数呢
def wrapper(func, *args, **kwargs):
... timing logic ...
response = func(*args, **kwargs)
... more timing logic
return response
并称之为:
wrapper(datacreator.createPizza, arg1, arg2, kwarg1=kwarg)
注意,您传递函数本身,但不调用它。我将创建一个
dataCreator
适配器类,其工作方式如下:
dataCreator
方法的methodswrap
列表\uuuu getattribute\uuuu()
,它将1:1映射到dataCreator
方法,将methods2wrap
中的方法包装到定时调试消息中列表
包装起来,并在其方法append
周围插入调试时间戳)
当然,您可以在这个示例的基础上进行构建,并将其参数化,以便在初始化时可以设置要包装的类和应该计时的方法
编辑:请注意,在每次调用\uuu getattribute\uuuu
时,都会重新分配TO_OVERRIDE
。这是故意的。如果将其作为类属性,\uuuuuu getattribute\uuuuu
将递归循环(您应该使用显式调用父\uuuuuu getattribute\uuuuu
方法来检索它,但这可能比从头开始重建列表要慢
HTH以下模板可能会有所帮助:
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
我们谈论的是带方法的函数还是类?我知道装饰器。但是我该装饰什么呢?createPizza和createBurger是他们这边的函数。@jollybox.de-我们谈论的是带方法的类。“这与DRY相反”。不完全是。您正在包装,而不是重复。是的,您的外观将随着基础库的运行而增长。这没关系。就像装饰师一样,对吧?这会起作用,但我必须编写一行代码,如
包装器(datacreator.createPizza,arg1,arg2,kwarg1=kwarg)
对于我调用的库中的每个函数。每当它们引入新功能时,我都必须添加另一行……我想知道是否有办法避免这种情况。@caribou-不完全是-Python装饰程序返回一个函数对象。但将其转换为装饰程序并不重要。@caribou-你没有对我的答案发表评论,但这是很简单的我的解决方案试图解决的问题。当然,您仍然需要指定要计时的方法,但它们的名称将是您必须插入的全部内容……或者我遗漏了什么吗?我主要想分析它们的一个库的调用,并在调试级别上输出到日志。似乎分析库可以做到这一点,所以我将这似乎不能处理返回的方法内的执行时间。在这个解决方案中,profiled_list.append('d'))
将只包括检索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
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