什么是用于管理配置参数的良好的可散列python类似dict的对象?

时间:2018-03-12 12:28:24

标签: python dictionary caching hashable

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

  • 因此,cfg必须是可以播放的hash(cfg1) == hash(cfg2) cfg1 == cfg2
  • 我也很乐意通过点符号访问配置参数,例如cfg.para1
  • 嵌套的条目会很好,但是一个级别就足够了,dictlistcfg中唯一不可取的对象。

我想做这样的事情:

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 

首先尝试

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

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

第二次尝试

在初始化OrderedDicts之前,从dic及其条目(在将其转换为元组的元组之后,......)中生成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内核之后),虽然条目相同,但散列不同。

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

0 个答案:

没有答案