【问题标题】:Class Decorator decorating method in python [duplicate]python中的类装饰器装饰方法[重复]
【发布时间】:2012-02-09 23:40:44
【问题描述】:

我正在尝试使用装饰器来记忆,而装饰器是一个类而不是一个函数,但我得到了错误

TypeError: seqLength() takes exactly 2 arguments (1 given)

我猜这与课程有关,但不确定那里出了什么问题。

代码:

import sys

class memoize(object):
    '''memoize decorator'''
    def __init__(self, func):
        self.func = func
        self.cache = {}
    def __call__(self, *args):
        try:
            return self.cache[args]
        except KeyError:
            value = self.func(self, *args)
            self.cache[args] = value
            return value

class collatz(object):
    def __init__(self, n):
        self.max = 1
        self.n = n
    @memoize
    def seqLength(self, n):
        if n>1:
            if n%2 == 0:
                return 1+self.seqLength(n/2)
            else:
                return 1+self.seqLength(3*n+1)
        else:
            return 1
    def maxLength(self):
        for n in xrange(1, self.n):
            l = self.seqLength(n)
            if l > self.max:
                self.max = n
        return self.max

n = int(sys.argv[1])
c = collatz(n)
print c.maxLength()

【问题讨论】:

    标签: python decorator memoization


    【解决方案1】:

    这在语法上令人困惑。尚不清楚 self.func 是您的 memoize 的一部分,还是属于其他类的其他对象的单独函数。 (你的意思是后者,顺便说一句)

            value = self.func(self, *args)
    

    这样做是为了明确the_func 只是一个函数,而不是 memoize 类的成员。

            the_func= self.func
            value= the_func( *args )
    

    这样可以防止混淆self. 绑定到的类。

    另外,请拼写为Memoize。带有前导大写字母。毕竟这是一个类定义。

    【讨论】:

      【解决方案2】:

      使用类作为装饰器很棘手,因为您必须正确实现the descriptor protocol(当前接受的答案没有。)一个更简单的解决方案是使用包装函数,因为它们会自动实现描述符协议正确。您的类的包装器等效项是:

      import functools
      
      def memoize(func):
          cache = {}
      
          @functools.wraps(func)
          def wrapper(*args):
              try:
                  return cache[args]
              except KeyError:
                  value = func(*args)
                  cache[args] = value
                  return value
          return wrapper
      

      当有这么多状态你想将它封装在一个类中时,你可以仍然使用包装函数,例如:

      import functools
      
      class _Memoize(object):
          '''memoize decorator helper class'''
          def __init__(self, func):
              self.func = func
              self.cache = {}
      
          def __call__(self, *args):
              try:
                  return self.cache[args]
              except KeyError:
                  value = self.func(*args)
                  self.cache[args] = value
                  return value
      
      def memoize(func):
          o = _Memoize(func)
          @functools.wraps(func)
          def wrapper(*args):
              return o(*args)
          return wrapper
      

      【讨论】:

        【解决方案3】:

        装饰器只是foo = decorator(foo) 的语法糖,因此在这种情况下,您最终会将seqLengthself 设为memoize 而不是collatz。您需要使用描述符。这段代码对我有用:

        class memoize(object):
            '''memoize descriptor'''
            def __init__(self, func):
                self.func = func
        
            def __get__(self, obj, type=None):
                return self.memoize_inst(obj, self.func)
        
            class memoize_inst(object):
                def __init__(self, inst, fget):
                    self.inst = inst
                    self.fget = fget
        
                    self.cache = {}
        
                def __call__(self, *args):
                    # if cache hit, done
                    if args in self.cache:
                        return self.cache[args]
                    # otherwise populate cache and return
                    self.cache[args] = self.fget(self.inst, *args)
                    return self.cache[args]
        

        有关描述符的更多信息:

        http://docs.python.org/howto/descriptor.html#descriptor-example

        【讨论】:

        • 太棒了!非常感谢。
        • 这...实际上无法正常工作。它存储 memoize 实例从中检索到的 last 实例,所以如果你这样做,例如,'m1 = o1.attr; m2 = o2.attr',调用'm1'实际上会调用'o2'作为“self”的方法。要使其正常工作,__get__ 方法必须返回一个 new 对象。但更好的是让装饰器成为一个函数。做对要容易得多。
        • 谢谢!修复了答案中的示例,此处演示了错误并修复了 impl:gist.github.com/AdamG/6049318
        猜你喜欢
        • 2020-01-11
        • 2014-01-14
        • 2019-09-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-14
        • 2011-10-04
        • 2020-01-02
        • 2011-11-20
        相关资源
        最近更新 更多