在字典参数中使用@ functools.lru_cache

问题描述

|| 我有一个方法(其中包括)以字典作为参数。该方法正在解析字符串,并且字典为某些子字符串提供了替换,因此它不必是可变的。 该函数在冗余元素上经常被调用,因此我认为将其缓存会提高效率。 但是,正如您可能已经猜到的那样,由于ѭ0是可变的,因此不可散列,所以ѭ1不能装饰我的函数。那么我该如何克服呢? 如果只需要标准库类和方法,则可以加分。理想情况下,如果它在标准库中存在某种ѭ2I,而我还没有看到过,那将是我的第一天。 PS::3ѭ只能在不得已的情况下使用,因为它需要大的语法转换。     

解决方法

        与其使用自定义的可哈希字典,不如使用它,避免重新发明轮子!这是一本冻结的字典,所有字典都是可哈希的。 https://pypi.org/project/frozendict/ 码:
def freezeargs(func):
    \"\"\"Transform mutable dictionnary
    Into immutable
    Useful to be compatible with cache
    \"\"\"

    @functools.wraps(func)
    def wrapped(*args,**kwargs):
        args = tuple([frozendict(arg) if isinstance(arg,dict) else arg for arg in args])
        kwargs = {k: frozendict(v) if isinstance(v,dict) else v for k,v in kwargs.items()}
        return func(*args,**kwargs)
    return wrapped
接着
@freezeargs
@lru_cache
def func(...):
    pass
代码来自@fast_cen的答案 注意:这不适用于递归数据结构;例如,您可能有一个参数\\是一个列表,该列表不可散列。邀请您进行包装的递归,使包装深入数据结构并冻结每个
dict
和每个
list
元组。 (我知道OP不再需要解决方案,但我来这里是寻找相同的解决方案,因此将其留给后代使用)     ,        像这样创建一个可哈希的
dict
类呢?
class HDict(dict):
    def __hash__(self):
        return hash(frozenset(self.items()))

substs = HDict({\'foo\': \'bar\',\'baz\': \'quz\'})
cache = {substs: True}
    ,        这是使用@mhyfritz技巧的装饰器。
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()))

    @functools.wraps(func)
    def wrapped(*args,**kwargs):
        args = tuple([HDict(arg) if isinstance(arg,dict) else arg for arg in args])
        kwargs = {k: HDict(v) if isinstance(v,**kwargs)
    return wrapped
只需在您的lru_cache之前添加它即可。
@hash_dict
@functools.lru_cache()
def your_function():
    ...
    ,        子类化ѭ3sub并添加access13ѭ的访问方式怎么样?
class X(namedtuple(\"Y\",\"a b c\")):
    def __getitem__(self,item):
        if isinstance(item,int):
            return super(X,self).__getitem__(item)
        return getattr(self,item)
    ,        这是可以像
functools.lru_cache
这样使用的装饰器。但这是针对仅采用一个参数的函数,该参数是具有可哈希值的平面映射,固定为16固定为16。对于您的用例,您将不得不调整此示例或客户代码。另外,要单独设置ѭ16,一个人必须实现另一个装饰器,但是由于我不需要它,我没有把头缠在这上面。
from functools import (_CacheInfo,_lru_cache_wrapper,lru_cache,partial,update_wrapper)
from typing import Any,Callable,Dict,Hashable

def lru_dict_arg_cache(func: Callable) -> Callable:
    def unpacking_func(func: Callable,arg: frozenset) -> Any:
        return func(dict(arg))

    _unpacking_func = partial(unpacking_func,func)
    _cached_unpacking_func = \\
        _lru_cache_wrapper(_unpacking_func,64,False,_CacheInfo)

    def packing_func(arg: Dict[Hashable,Hashable]) -> Any:
        return _cached_unpacking_func(frozenset(arg.items()))

    update_wrapper(packing_func,func)
    packing_func.cache_info = _cached_unpacking_func.cache_info
    return packing_func


@lru_dict_arg_cache
def uppercase_keys(arg: dict) -> dict:
    \"\"\" Yelling keys. \"\"\"
    return {k.upper(): v for k,v in arg.items()}


assert uppercase_keys.__name__ == \'uppercase_keys\'
assert uppercase_keys.__doc__ == \' Yelling keys. \'
assert uppercase_keys({\'ham\': \'spam\'}) == {\'HAM\': \'spam\'}
assert uppercase_keys({\'ham\': \'spam\'}) == {\'HAM\': \'spam\'}
cache_info = uppercase_keys.cache_info()
assert cache_info.hits == 1
assert cache_info.misses == 1
assert cache_info.maxsize == 64
assert cache_info.currsize == 1
assert uppercase_keys({\'foo\': \'bar\'}) == {\'FOO\': \'bar\'}
assert uppercase_keys({\'foo\': \'baz\'}) == {\'FOO\': \'baz\'}
cache_info = uppercase_keys.cache_info()
assert cache_info.hits == 1
assert cache_info.misses == 3
assert cache_info.currsize == 3
对于更通用的方法,可以使用第三方库中的装饰器@ cachetools.cache,并将适当的功能设置为“ 19”。     ,        在决定暂时删除我们的用例的lru缓存后,我们仍然提出了一个解决方案。该装饰器使用json序列化和反序列化发送到缓存的args / kwargs。适用于任意数量的args。将其用作函数的修饰器,而不是@lru_cache。最大大小设置为1024。
def hashable_lru(func):
    cache = lru_cache(maxsize=1024)

    def deserialise(value):
        try:
            return json.loads(value)
        except Exception:
            return value

    def func_with_serialized_params(*args,**kwargs):
        _args = tuple([deserialise(arg) for arg in args])
        _kwargs = {k: deserialise(v) for k,v in kwargs.items()}
        return func(*_args,**_kwargs)

    cached_function = cache(func_with_serialized_params)

    @wraps(func)
    def lru_decorator(*args,**kwargs):
        _args = tuple([json.dumps(arg,sort_keys=True) if type(arg) in (list,dict) else arg for arg in args])
        _kwargs = {k: json.dumps(v,sort_keys=True) if type(v) in (list,v in kwargs.items()}
        return cached_function(*_args,**_kwargs)
    lru_decorator.cache_info = cached_function.cache_info
    lru_decorator.cache_clear = cached_function.cache_clear
    return lru_decorator