1

我正在使用这里的 memoize 配方,并针对返回 2 个值的函数稍微修改了它。我使用这个包装器来创建两个单独的函数,它们分别返回第一个和第二个值,但是函数评估被缓存,因此当使用相同的参数调用任何一个返回的函数时没有开销。这是此包装器的代码。

def memoize(obj, cache_limit=10):
    '''
    This function caches the return value each time it is called. partial() is used to return the appropriate value.
    Cache size is limited to 10
    See here for details on this design pattern: https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
    '''
    cache = obj.cache = {}
    key_cache = collections.deque()

    @functools.wraps(obj)
    def memoizer(which, *args, **kwargs):
        key = str(args)
        if key not in cache:
            cache[key] = obj(*args, **kwargs)
            key_cache.append(key)
            if len(key_cache) >= cache_limit:
                del cache[key_cache.popleft()]
        return cache[key][which]
    return functools.partial(memoizer, 0), functools.partial(memoizer, 1)

现在,我正在尝试f在以这种方式在类中定义的函数上使用它:

class test_function:
    def __init__(self):
        ''''''

    def f(self,x):
        return 2*x, 3*x

我这样称呼它

a = test_function()
f_v1, f_v2 = memoize(a.f)

如果成功f_v1(x)将返回2x并将f_v2(x)返回3x。但这失败并出现错误:

AttributeError: 'instancemethod' object has no attribute 'cache'

如果函数在类之外声明,我的代码可以正常工作。我错过了什么?我正在使用Python 2.7.

4

1 回答 1

2

方法是一种不同于函数的对象(一个instancemethod对象,如错误消息所示;这种类型可用作types.MethodType)。与函数对象不同,实例方法没有__dict__,因此您不能在它们上设置任意属性;您无法创建在该方法上obj.someMethod.someAttribute = "blah"调用的自己的自定义属性。someAttribute

我不清楚为什么要将缓存存储在对象上,因为您从未真正从那里访问过它。如果您只使用局部变量cache,它将保存在闭包中并且可以正常工作:

def memoize(obj, cache_limit=10):
    '''
    This function caches the return value each time it is called. partial() is used to return the appropriate value.
    Cache size is limited to 10
    See here for details on this design pattern: https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
    '''
    cache = {}
    key_cache = collections.deque()

    @functools.wraps(obj)
    def memoizer(which, *args, **kwargs):
        key = str(args)
        if key not in cache:
            cache[key] = obj(*args, **kwargs)
            key_cache.append(key)
            if len(key_cache) >= cache_limit:
                del cache[key_cache.popleft()]
        return cache[key][which]
    return functools.partial(memoizer, 0), functools.partial(memoizer, 1)

>>> a = test_function()
... f_v1, f_v2 = memoize(a.f)
>>> f_v1(2)
4
>>> f_v2(2)
6
于 2015-02-24T19:30:37.117 回答