0

为了缓存函数(现在我正在使用klepto),我将一个配置对象传递cfg给函数,只有在cfg更改时才计算返回值。

  • 因此,cfg必须是可散列的,并且hash(cfg1) == hash(cfg2)if cfg1 == cfg2
  • 我也很想通过点符号访问配置参数,例如cfg.para1.
  • 嵌套条目会很好,但是一个级别就足够了,我可以接受dict并且listcfg.

我想做这样的事情:

from klepto import lru_cache
from klept.keymaps import hasher

class MyClass:
    def __init__(self, cfg)
        self.cfg = cfg

    def my_func(self):
        return self._my_func(self.cfg)

    @lru_cache(keymap=hasher, ignore=(self,))
    def _my_func(self, cfg):
        a = cfg.a
        b = cfg.b.c[0]
        return expensive_func(a, b)

cfg_dic = {
    'a': 1, 
    'b': {'c': [1,2,3]}
}

cfg = give_me_the_hashable_object_i_need(cfg_dic)

my_obj = MyClass(cfg)

my_obj.my_func()    #evaluate only if cfg was not (recently) used before 

第一次尝试

使用Box()python-box 中的对象,由常规(嵌套)python dict 初始化cfg_dic

from box import Box
cfg = Box(cfg_dic, frozen_box=True, box_it_up=True)

第二次尝试

Make OrderedDictsfromdic和它的条目(在把它变成元组的元组之后,..)在初始化之前是Box这样的:

from collections import OrderedDict
from box import Box

def to_tuple(dic_or_list):
    l = []
    if isinstance(dic_or_list, list):
        for v in sorted(dic_or_list):
            try:
                hash(v)
                l.append(v)
            except TypeError:
                l.append(to_tuple(v))
    elif isinstance(dic_or_list, dict):
        for k, v in sorted(dic_or_list.items()):
            try:
                hash(v)
                l.append((k, v))
            except TypeError:
                l.append((k, to_tuple(v)))
    else:
        raise ValueError('argument must be one of "dict", "list"')
    return tuple(l)

def second_level_odict(tup):
    if len(tup) == 2:
        k_1, v_1 = tup
        if isinstance(v_1, tuple):
            for v_2 in v_1:
                if len(v_2) != 2:
                    return (k_1, v_1)
            return (k_1, (OrderedDict(v_1)))
        else:
            return (k_1, v_1)
    else:
        return tup

def first_level_odict(tup):
    l = []
    for t in tup:
        l.append(second_level_odict(t))
    return OrderedDict(l)

tup = to_tuple(dic)
odic = first_level_odict(tup)

cfg = Box(odic, frozen_box=True, box_it_up=True)

第三次尝试

放弃box并使 dict 可散列。

class hashabledict(dict):
    def __hash__(self):
        return hash(tuple(sorted(self.items())))

class hashablelist(list):
    def __hash__(self):
        return hash(tuple(sorted(self)))

def make_hashable(dic):
    for k, v in dic.items():
        if isinstance(v, dict):
            dic[k] = hashabledict(v)
        elif isinstance(v, list):
            dic[k] = hashablelist(v)
    return hashabledict(dic)

更新第四次尝试

我还尝试将cfg Box()作为带有排序键的 json-string 传递:

def my_func(self):
    return self._my_func(self.cfg.to_json(sort_keys=True))

@lru_cache(keymap=hasher, ignore=(self,))
def _my_func(self, cfg_string):
    cfg =  Box().from_json(cfg_string)
    a = cfg.a
    b = cfg.b.c[0]
    return expensive_func(a, b)

所有这些都失败了,因为(例如,在重新启动 IPython 内核之后),尽管条目相同,但哈希值却不同。

我愿意接受任何建议,非常感谢!

4

0 回答 0