将@ functools.lru_cache与字典参数一起使用

Evp*_*pok 22 python dictionary python-3.x hashable

我有一个方法,其中(以及其他)字典作为参数.该方法是解析字符串,字典提供了一些子字符串的替换,因此它不必是可变的.

这个函数经常被调用,而且在冗余元素上,所以我认为缓存它会提高它的效率.

但是,正如你可能已经猜到的那样,因为它dict是可变的,因而不能清洗,@functools.lru_cache所以无法装饰我的功能.那我怎么能克服这个呢?

如果只需要标准的库类和方法,则可以获得奖励积分.理想情况下,如果它存在某种frozendict标准库,我还没有看到它会成为我的一天.

PS:namedtuple只有最后的手段,因为它需要大的语法转换.

mhy*_*itz 9

如何创建一个dict像这样的可散列类:

class HDict(dict):
    def __hash__(self):
        return hash(frozenset(self.items()))

substs = HDict({'foo': 'bar', 'baz': 'quz'})
cache = {substs: True}
Run Code Online (Sandbox Code Playgroud)

  • 现在没有_easy_方式出现在我的脑海里.你当然可以放下dict并沿途转换不可变量(dicts to frozensets,list to to tuples等)...... (3认同)
  • 像魅力一样工作,虽然只有当dict的项目都是hashables,但这是我的情况.但是,你有一些技巧来处理非hashables`self.items()`? (2认同)

fas*_*cen 8

这是一个使用@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, dict) else v for k, v in kwargs.items()}
        return func(*args, **kwargs)
    return wrapped
Run Code Online (Sandbox Code Playgroud)

只需在lru_cache之前添加它.

@hash_dict
@functools.lru_cache()
def your_function():
    ...
Run Code Online (Sandbox Code Playgroud)

  • 要提供`clear_cache()` 和`cache_info()` 调用,只需在返回之前在`wrapped` 上添加这些函数。类似于 `wrapper.cache_info = func.cache_info` 和 `wrapper.cache_clear = func.cache_clear` (3认同)

Ced*_*dar 5

与其使用自定义的可哈希字典,不如使用它,避免重新发明轮子!这是一本冻结的字典,都是可哈希的。

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
Run Code Online (Sandbox Code Playgroud)

然后

@freezeargs
@lru_cache
def func(...):
    pass
Run Code Online (Sandbox Code Playgroud)

代码来自@fast_cen的答案

注意:这不适用于递归数据结构;例如,您可能有一个参数列表,这是不可哈希的。邀请您进行包装的递归,这样包装就可以深入数据结构并制作每个dict冻结的list元组。

(我知道OP不再需要解决方案,但我来这里是寻找相同的解决方案,因此将其留给后代使用)