Python:将所有函数包装在库中

时间:2011-07-06 20:07:06

标签: python wrapper

我们使用另一个内部团队提供的库。 (现在开始摇摇欲坠的比喻)

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调用的每个函数执行几行代码的方法。必须有一些方法可以通过一个中间类来做到这一点,中间类的方法可以动态定义 - 或者更确切地说是未定义的,对吗?

4 个答案:

答案 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适配器类,它的工作原理如下:

  1. methods2wrap获取需要包含在调试/计时功能中的dataCreator方法列表。
  2. 覆盖__getattribute__(),将1:1映射到dataCreator方法,将methods2wrap中的方法包装到计时调试消息中。
  3. 概念验证代码(示例包装类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