【问题标题】:How to replace decorator with method in python class如何用python类中的方法替换装饰器
【发布时间】:2013-12-27 04:14:06
【问题描述】:

假设我有以下代码,我使用 get_duration 装饰器打印出方法持续了多长时间。

import datetime
import time

def get_duration(function):
  def wrapper(*args, **kwargs):
    start = datetime.datetime.now()
    print "starting process %s()" %function.__name__
    function(*args, **kwargs)
    end = datetime.datetime.now()
    print "Process lasted: ", end - start
  return wrapper

class Operations(object):

  def __init__(self, a, b):
    self.a = a
    self.b = b

  @get_duration
  def add(self):
    time.sleep(1)
    print self.a + self.b

  @get_duration
  def sub(self):
    time.sleep(1)
    print self.a - self.b


my_class = Operations(2, 98)
my_class.add()
my_class.sub()

输出

>> starting process add()
>> 100
>> Process lasted:  0:00:01.000260
>> starting process sub()
>> -96
>> Process lasted:  0:00:01.001161

我可以用类方法或类似的东西替换它吗(下面的伪代码):

my_class = Operations(2, 98)
my_class.add.get_duration #runs add(), prints duration
my_class.add #runs add() does not print duration

【问题讨论】:

  • 你应该使用 timeit 模块来处理这类事情。
  • 谢谢,但这只是装饰器的一个例子。我对这个过程的具体时间不感兴趣。
  • 也许你可以直接使用装饰器,像这样:get_duration(my_class.add)()?

标签: python methods decorator


【解决方案1】:

你可以这样做:

def get_duration(function):
  def wrapper(*args, **kwargs):
    kwargs['self'].elapsed_time = timeit.timeit('function(*args, **kwargs)',
                                      "from __main__ import function, args, kwargs",
                                      number=1)
  return wrapper

然后你可以阅读elapsed_time var 来获取时间。

【讨论】:

    【解决方案2】:

    要完全按照您的意愿行事,您可以通过可调用类来装饰此方法。

    class TimeElapser  :
       def __init__(self, function_to_wrap) :
           self.__call__ = function_to_wrap
           self.get_duration = get_duration(function_to_wrap)
    

    例子:

    class Foo :
    
        @TimeElapser
        def do_something(self, ....) :
           ...
    
    foo = Foo()
    foo.do_something() #Does something
    foo.do_something.get_duration() # Does something and print duration
    

    【讨论】:

    • 而不是每次都重新应用装饰器,您应该在构造函数中使用self.get_duration = get_duration(function) 分配它。否则,这是迄今为止发布的最干净的解决方案。
    • 我不确定我是否理解它是如何工作的。可以发个例子吗?
    • 这是有道理的,但它只有在 do_something() 定义时没有参数才有效......我如何将它调整为 *args、**kwargs?
    • 所以,如果我执行 'def do_something(): print "bar" ' 一切正常,但如果我执行 'def do_something(argument): print "bar" + argument' 然后调用 foo .do_something('my_argument').get_duration() 我会得到“AttributeError: 'NoneType' object has no attribute 'get_duration'”
    • 您可以使用 foo.do_something.get_duration('my_argument')。可能我误解了你的任务,就像@Ashwini Chaudhary 一样。请将这些示例添加到您的问题中,因为现在的表述尚不清楚。
    【解决方案3】:

    这样的?

    def map_func(function):
        def wrapper(*args, **kwargs):
            start = datetime.datetime.now()
            print "starting process %s()" %function.__name__
            function(*args, **kwargs)
            end = datetime.datetime.now()
            print "Process lasted: ", end - start
        function.get_duration = wrapper
        return function
    

    演示:

    >>> c = Operations(100, 200)
    >>> c.add()
    c.300
    >>> c.sub()
    -100
    >>> c.add.get_duration(c)
    starting process add()
    300
    Process lasted:  0:00:01.001584
    >>> c.sub.get_duration(c)
    starting process sub()
    -100
    Process lasted:  0:00:01.003768
    

    【讨论】:

      【解决方案4】:

      我认为这是一个很好的元编程用例:

      import types
      
      def my_dec(f):
          print "decorated"
          return f
      
      class MyMetaprogrammingDecoratorClass(object):
          def __getattr__(self, attribute):
              try:
                  ## we should honor inherited classes that may have defined __getattr__
                  return super(MyMetaprogrammingDecoratorClass, self).__getattr__(attribute)
              except AttributeError:
                  if attribute.endswith("_decorated"):
                      output = object.__getattribute__(self, attribute.replace("_decorated",""))
                      if isinstance(output, types.MethodType):
                          return my_dec(output)
                      else:
                          raise AttributeError
                  else:
                      raise AttributeError
      
      class MyCustomClass(MyMetaprogrammingDecoratorClass):
          def f(self):
              return 1
      
      g = MyCustomClass()
      print g.f()
      >> 1
      print g.f_decorated()
      >> decorated
      >> 1
      

      因此,您可以使用_decorated 对任何方法进行后缀,并且该类将知道使用my_dec 装饰该方法(如果存在)。

      【讨论】:

        猜你喜欢
        • 2021-11-20
        • 2012-02-09
        • 1970-01-01
        • 2018-07-14
        • 2014-01-14
        • 2020-01-11
        • 2019-02-03
        相关资源
        最近更新 更多