当其记忆参数之一应该是实例变量时如何记忆方法
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}
要记忆 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}