当其记忆参数之一应该是实例变量时如何记忆方法

How to memoize method when one of its memoized parameters should be an instance variable

要记忆 f(x) 我可以使用 functools.lru_cache():

class A(object):

    def __init(self):
        self.time = 10  # This changes in the various spots in the program

    @functools.lru_cache(maxsize=None)
    def f(self, x):
        # Lots of code
        # ...
        # ...
        return x * some_other_func(self.time)

(根据我的理解)lru_cache() 创建一个字典,其中包含各种 x 作为键和它们对应的 f(x) 作为值,因此它 returns 存储的值如果我用相同的参数值调用 f(),而不是重新计算它。但是,这不是我需要的。

我的目标是针对 both x and [=20] 的不同值记忆 f() 值=].


使用以下代码实现我的目标:

class A(object):

    def __init(self):
        self.time = 10

    @functools.lru_cache(maxsize=None)
    def g(self, x, t):
        # Lots of code
        # ...
        # ...
        return x * some_other_func(self.time)

    def f(self, x):
        return self.g(x=x, t=self.time)

现在我不再直接记忆 f(x),而是记忆 g(),它总是用 t=self.time 调用。

但是我不确定这是否是最干净的解决方案。我希望只使用装饰器进行记忆,而不必创建中间方法。

有没有更简单的方法来实现上述目标? (我将不得不对几种方法执行上述操作,因此我正在寻找尽可能干净的解决方案)

如果您想将其实现为方法装饰器,这里有一个选项将与状态相关的属性名称作为参数:

from functools import wraps

def stateful_memoize(*attrs):
    """Memoization that respects specified instance state."""
    def decorator(method):
        @wraps(method)
        def wrapper(self, *args):
            state = args + tuple(getattr(self, attr)
                                 for attr in attrs)
            if state not in wrapper.cache:
                wrapper.cache[state] = method(self, *args)
            return wrapper.cache[state]
        wrapper.cache = {}
        return wrapper
    return decorator

此简单版本不适用于关键字方法参数,但其他方面应该没问题。使用中:

>>> class A(object):

    def __init__(self):
        self.time = 10

    @stateful_memoize('time')
    def f(self, x):
        print('Calling f with x={!r}, self.time={!r}'.format(x, self.time))
        return x * self.time

>>> a = A()
>>> a.f(1)
Calling f with x=1, self.time=10
10
>>> a.f(1)
10
>>> a.time = 5
>>> a.f(1)
Calling f with x=1, self.time=5
5
>>> a.time = 10
>>> a.f(1)
10
>>> a.f(2)
Calling f with x=2, self.time=10
20
>>> a.f.cache
{(1, 10): 10, (1, 5): 5, (2, 10): 20}