【问题标题】:How to memoize method when one of its memoized parameters should be an instance variable当其记忆参数之一应该是实例变量时如何记忆方法
【发布时间】:2015-11-22 20:21:25
【问题描述】:

要记住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) 作为值的字典,因此如果我使用相同的参数值调用f(),它会返回存储的值,而不是重新计算它。然而,这不是我需要的。

我的目标是为 both x self.time 的不同值记住 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 调用。

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

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

【问题讨论】:

  • 即使self 在设置默认参数时在范围内,缓存也会忽略默认值。要么坚持现有的,要么自己实现缓存。
  • @jonrsharpe 抱歉,这不是我的意思。我从帖子中删除了它。我需要的是简单地考虑一个不属于f() 参数但不知道如何解释的变量。
  • 您的解决方案对我来说似乎很好
  • 您需要编写自己的记忆装饰器。你可以通过谷歌搜索找到很多方法,因为在lru_cache 被添加到标准库之前,人们一直都在这样做。
  • 您已经在以一种明显的方式执行此操作,即使用第二种方法缓存并由第一种方法调用。否则,您必须自己编写自定义缓存,以考虑实例的当前状态。

标签: python class python-3.4 memoization


【解决方案1】:

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

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}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-10
    • 2020-08-22
    • 1970-01-01
    • 1970-01-01
    • 2011-03-14
    • 2015-02-17
    相关资源
    最近更新 更多