将 class 成员函数的包装器记忆到 return 部分值

Memoize wrapper for class member functions to return partial values

我正在使用 here 的 memoize 配方,并针对 returns 2 值的函数对其进行了稍微修改。我使用这个包装器创建了两个单独的函数,它们 return 分别是第一个值和第二个值,但是函数计算被缓存起来,这样当使用同样的论点。这是此包装器的代码。

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 中以这种方式定义:

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) 将 return 2x 并且 f_v2(x) 将 return 3x。但这失败并出现错误:

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

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

方法是一种不同于函数的对象(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