所以我最近问了一个关于记忆的问题,并得到了一些很好的答案,现在我想将其带入一个新的高度。经过大量的搜索之后,我找不到能够装饰带有关键字参数的函数的备忘录装饰器的参考实现。实际上,大多数人只是将*args
用作高速缓存查找的键,这意味着如果您想记住一个接受列表或字典作为参数的函数,它也会中断。
就我而言,该函数的第一个参数本身就是一个唯一标识符,适合用作缓存查找的dict键,但是我希望能够使用关键字参数并仍然访问相同的缓存。我的意思是,my_func('unique_id', 10)
和my_func(foo=10, func_id='unique_id')
都应返回相同的缓存结果。
为了做到这一点,我们需要一种简洁明了的方式来表达'检查kwargs对应于第一个参数的那个关键字)。这是我想出的:
class memoize(object):
def __init__(self, cls):
if type(cls) is FunctionType:
# Let's just pretend that the function you gave us is a class.
cls.instances = {}
cls.__init__ = cls
self.cls = cls
self.__dict__.update(cls.__dict__)
def __call__(self, *args, **kwargs):
"""Return a cached instance of the appropriate class if it exists."""
# This is some dark magic we're using here, but it's how we discover
# that the first argument to Photograph.__init__ is 'filename', but the
# first argument to Camera.__init__ is 'camera_id' in a general way.
delta = 2 if type(self.cls) is FunctionType else 1
first_keyword_arg = [k
for k, v in inspect.getcallargs(
self.cls.__init__,
'self',
'first argument',
*['subsequent args'] * (len(args) + len(kwargs) - delta)).items()
if v == 'first argument'][0]
key = kwargs.get(first_keyword_arg) or args[0]
print key
if key not in self.cls.instances:
self.cls.instances[key] = self.cls(*args, **kwargs)
return self.cls.instances[key]
[疯狂的事情是,这确实有效。例如,如果您这样装饰:
@memoize
class FooBar:
instances = {}
def __init__(self, unique_id, irrelevant=None):
print id(self)
然后从您的代码中,您可以调用FooBar('12345', 20)
或FooBar(irrelevant=20, unique_id='12345')
,并且实际上获得FooBar的相同实例。然后,您可以为第一个参数定义一个具有不同名称的不同类,因为它以一种通用的方式工作(即,装饰器不需要知道有关其装饰的类的任何特定信息即可使它工作)。 >
问题是,这真是一团糟;-)
之所以有效,是因为inspect.getcallargs
返回一个将已定义的关键字映射到您提供的参数的字典,因此我为其提供了一些虚假参数,然后检查该字典以获取第一个传递的参数。
如果甚至存在这样的事情,会更好的是inspect.getcallargs
的类似物,该方法返回两种参数统一为参数列表而不是关键字参数的字典。那将允许这样的事情:
def __call__(self, *args, **kwargs): key = inspect.getcallargsaslist(self.cls.__init__, None, *args, **kwargs)[1] if key not in self.cls.instances: self.cls.instances[key] = self.cls(*args, **kwargs) return self.cls.instances[key]
我可以看到的另一种解决方法是直接使用
inspect.getcallargs
提供的dict作为查找缓存键,但这需要一种可重复的方法来从相同的哈希中生成相同的字符串,这是我所听到的不能依靠(我想我必须在对键进行排序后自己构造字符串)。
有人对此有任何想法吗?想用关键字参数调用函数并缓存结果是否错误?还是非常难?
所以我最近问了一个关于记忆的问题,并得到了一些很好的答案,现在我想将其带入一个新的高度。经过大量的谷歌搜索,我找不到...的参考实现...
我建议以下内容:
尝试lru_cache:
您可以看一下我的包:https://github.com/Yiling-J/cacheme,实际上我对所有的args / kwargs使用了一个容器: