Python中的类装饰器用于装饰方法。

3
我正在尝试使用一个类作为装饰器来进行记忆化,而不是一个函数,但是我遇到了错误。
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()
3个回答

2
这段话在语法上令人困惑。不清楚self.func是你的记忆化的一部分还是属于其他类的某个对象中的一个单独函数(你指的是后者)。
        value = self.func(self, *args)

这样做可以清楚地表明the_func只是一个函数,而不是memoize类的成员。

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

这种方法可以避免混淆self.所绑定的类。

另外,请将其拼写为Memoize,首字母大写。毕竟它是一个类定义。


2

使用类作为装饰器是棘手的,因为您必须正确实现描述符协议(当前接受的答案不是这样)。一个更简单的解决方案是使用包装函数,因为它们自动正确实现描述符协议。您的类的包装器等效物如下:

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

-1

装饰器只是对 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


2
这个...实际上并没有正确运行。它存储了记忆化实例最后一次被检索的实例,因此,如果你做了' m1 = o1.attr; m2 = o2.attr ',调用'm1'将实际上调用'o2'作为"self"的方法。为了使其正常工作,__get__方法必须返回一个对象。但更好的方法是将装饰器变成一个函数。这样更容易正确理解。 - Thomas Wouters
谢谢!已经修复了答案中的示例,演示了错误和修复的实现在这里:https://gist.github.com/AdamG/6049318 - AdamKG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接