这是我的解决方案,根据@dmulter 的建议改编自Foundation for rolling your own LRU cache variants。
我添加了update_wrapper 并覆盖了__repr__ 以使其作为func 的包装器更好地工作。然后添加了一些方法来从缓存中删除项目(通过传入调用它的相同参数),或者直接替换缓存中的值(通过传入要替换的值,后跟你将使用的相同参数再次调用函数)。我还通过对参数进行散列添加了对**kwargs 的一些支持。我还没有对它进行非常彻底的测试,但它似乎工作得很好。希望这对下一个寻找此功能的人有用。
我觉得这个应该可以像@lru_cache一样使用。
from functools import update_wrapper
class MyLRU:
def __init__(self, func, maxsize=128):
self.cache = collections.OrderedDict()
self.func = func
self.maxsize = maxsize
update_wrapper(self, self.func)
def __call__(self, *args, **kwargs):
cache = self.cache
key = self._generate_hash_key(*args, **kwargs)
if key in cache:
cache.move_to_end(key)
return cache[key]
result = self.func(*args, **kwargs)
cache[key] = result
if len(cache) > self.maxsize:
cache.popitem(last=False)
return result
def __repr__(self):
return self.func.__repr__()
def clear_cache(self):
self.cache.clear()
def cache_remove(self, *args, **kwargs):
"""Remove an item from the cache by passing the same args and kwargs"""
key = self._generate_hash_key(*args, **kwargs)
if key in self.cache:
self.cache.pop(key)
def cache_replace(self, value, *args, **kwargs):
key = self._generate_hash_key(*args, **kwargs)
self.cache[key] = value
@staticmethod
def _generate_hash_key(*args, **kwargs):
key = hash(args)+hash(frozenset(sorted(kwargs.items())))
return key
例如
@MyLRU
def test(a, b=1, c=2):
print(f'a={a}, b={b}, c={c}')
return (a,b,c)
test(1, 2, 3) # prints and returns
test(1, 2, 3) # only returns (i.e. using cache)
test(1, b=2, c=3) # still prints, but this is a limitation with lru_cache as well
test(1, c=3, b=2) # only returns (an improvement on lru_cache behaviour I think)
test(3, 4, 5) # prints and returns
test.cache_remove(1,2,3)
test(1, 2, 3) # prints again
test(3, 4, 5) # only returns (so the rest of the cache wasn't cleared)
test.cache_replace('hi there', 1, 2, 3)
test(1, 2, 3) # only returns 'hi there' (so cache was replaced)