在Python中为方法装饰的类装饰器

3 投票
3 回答
852 浏览
提问于 2025-04-17 10:12

我正在尝试使用一个装饰器来实现记忆化,而这个装饰器是一个类而不是一个函数,但我遇到了错误。

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

把一个类当作装饰器来用其实挺复杂的,因为你需要正确实现描述符协议(而目前的答案并没有做到这一点)。其实有一个简单得多的解决办法,就是使用一个包装函数,因为包装函数会自动正确实现描述符协议。你类的包装函数版本可以这样写:

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
2

这段话有点让人困惑,从语法上看不太清楚 self.func 是你自己定义的记忆化函数的一部分,还是属于其他类的某个对象的一个单独函数。(顺便说一下,你是指后者)

        value = self.func(self, *args)

这样做可以让人更清楚 the_func 只是一个普通的函数,而不是记忆化类的成员。

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

这样可以避免对 self. 绑定的类产生混淆。

另外,请记得把它写成 Memoize,首字母要大写。毕竟这是一个类的定义。

-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

撰写回答