我正在尝试在Python3中使用lru_cache
来加速对Salesforce数据库的常见查询。以下是应该
当我尝试这段代码时,缓存可以用于调用没有参数的函数,但它似乎并没有用参数缓存函数调用。另外,我不知道如何为装饰函数订购装饰器。
注意,我在这里使用类和静态方法的类,因此我可以覆盖get
的不同子类的get_all
和Resource
方法。
请解释我做错了什么或者做得更好。
from functools import lru_cache
from functools import wraps
class Resource(object):
def hash_dict(func):
"""Transform mutable dictionnary
Into immutable
Useful to be compatible with cache
"""
class HDict(dict):
def __hash__(self):
return hash(frozenset(self.items()))
@wraps(func)
def wrapped(*args, **kwargs):
args = tuple([HDict(arg) if isinstance(arg, dict) else arg for arg in args])
kwargs = {}
for k, v in kwargs.items():
if isinstance(v, dict):
kwargs[k] = HDict(v)
elif isinstance(v, list):
kwargs[k] = tuple(v)
else:
kwargs[k] = v
return func(*args, **kwargs)
return wrapped
@staticmethod
@hash_dict
@lru_cache
def get(cls, resource_id, lang='en', fields=None):
pass
@classmethod
@hash_dict
@lru_cache
def get_all(cls, lang='en', filters=None, fields=None):
pass
答案 0 :(得分:1)
如果我来不及为你而迟到,那是我的回应。您有几处错误。
首先,您要覆盖args
函数定义中的kwargs
和wapped
。这实际上是删除您的函数参数。
第二,您只在kwargs
情况下创建不可变列表,而不在args
情况下。
此外,lru_cache
是一个修饰符构造函数,因此必须称为@lru_cache()
。没有它,我不知道它如何对您起作用。
此外,您将def get(cls, ...
声明为@staticmethod
,然后它将不会收到cls
参数。
但是最重要的,如this medium article中所述,在类内部定义装饰器并非易事。 我得到您正在尝试做的事情:通过继承来强制进行缓存,尽管这似乎是个好主意,但它无法正常工作。如果您重写了一个函数,则必须再次使用缓存重新对其进行重新装饰,这会错过在类内声明装饰器的意义。
总结一下,我将省去自己的麻烦,并在另一个类中声明装饰器,并在其他地方使用它。但是要小心,因为缓存类方法也不是很简单。
几周前,我处于类似情况,想要缓存一个接受numpy数组的函数。我根据this implementation提出了this so response。我只是将数组转换为元组,然后再次对其进行重构(因为我需要将其作为最后的可变数组)。
def np_cache(*args, **kwargs):
"""LRU cache implementation for functions whose FIRST parameter is a numpy array
>>> array = np.array([[1, 2, 3], [4, 5, 6]])
>>> @np_cache(maxsize=256)
... def multiply(array, factor):
... print("Calculating...")
... return factor*array
>>> multiply(array, 2)
Calculating...
array([[ 2, 4, 6],
[ 8, 10, 12]])
>>> multiply(array, 2)
array([[ 2, 4, 6],
[ 8, 10, 12]])
>>> multiply.cache_info()
CacheInfo(hits=1, misses=1, maxsize=256, currsize=1)
"""
def decorator(function):
@wraps(function)
def wrapper(np_array, *args, **kwargs):
hashable_array = array_to_tuple(np_array)
return cached_wrapper(hashable_array, *args, **kwargs)
@lru_cache(*args, **kwargs)
def cached_wrapper(hashable_array, *args, **kwargs):
array = np.array(hashable_array)
return function(array, *args, **kwargs)
def array_to_tuple(np_array):
"""Iterates recursivelly."""
try:
return tuple(array_to_tuple(_) for _ in np_array)
except TypeError:
return np_array
# copy lru_cache attributes over too
wrapper.cache_info = cached_wrapper.cache_info
wrapper.cache_clear = cached_wrapper.cache_clear
return wrapper
return decorator
虽然这不能直接解决您的问题,但可以很容易地将其推广为任意数量的输入参数。
答案 1 :(得分:1)
这应该是您正在寻找的答案:https://ring-cache.readthedocs.io/en/latest/quickstart.html#method-classmethod-staticmethod
lru_cache
仅支持简单功能。 Ring
提供了非常相似的界面,但包括任何种类的描述符支持。
class Page(object):
(...)
@ring.lru()
@classmethod
def class_content(cls):
return cls.base_content
@ring.lru()
@staticmethod
def example_dot_com():
return requests.get('http://example.com').content
有关更多详细信息,请参见链接。请注意,该示例不是LRU。
答案 2 :(得分:0)
我不确定您为什么需要从youknowone的答案中获取其他软件包。 我尝试了以下操作,并按预期工作:
import functools
class A:
@staticmethod
@functools.lru_cache(maxsize=None)
def build(value):
print('Creating', value)
return A()
assert A.build('a') is A.build('a')
assert A.build('b') is A.build('b')
assert A.build('b') is not A.build('a')