为了缓存函数(现在我正在使用klepto),我将一个配置对象传递cfg
给函数,只有在cfg
更改时才计算返回值。
- 因此,
cfg
必须是可散列的,并且hash(cfg1) == hash(cfg2)
ifcfg1 == cfg2
。 - 我也很想通过点符号访问配置参数,例如
cfg.para1
. - 嵌套条目会很好,但是一个级别就足够了,我可以接受
dict
并且list
是cfg
.
我想做这样的事情:
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 OrderedDicts
fromdic
和它的条目(在把它变成元组的元组之后,..)在初始化之前是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 内核之后),尽管条目相同,但哈希值却不同。
我愿意接受任何建议,非常感谢!