为了缓存目的,我需要从字典中存在的GET参数生成一个缓存键。
目前,我正在使用sha1(repr(sorted(my_dict.items()))) (sha1()是一个内部使用hashlib的方便方法),但我很好奇是否有更好的方法。
为了缓存目的,我需要从字典中存在的GET参数生成一个缓存键。
目前,我正在使用sha1(repr(sorted(my_dict.items()))) (sha1()是一个内部使用hashlib的方便方法),但我很好奇是否有更好的方法。
当前回答
虽然hash(frozenset(x.items())和hash(tuple(sorted(x.items()))可以工作,但分配和复制所有键-值对需要做很多工作。哈希函数应该避免大量的内存分配。
一点数学知识能帮上忙。大多数哈希函数的问题是他们认为顺序很重要。要对无序结构进行哈希,需要一个交换操作。乘法运算不能很好地工作,因为任何元素哈希到0都意味着整个乘积为0。位&和|倾向于所有的0或1。有两个很好的候选:加法和异或。
from functools import reduce
from operator import xor
class hashable(dict):
def __hash__(self):
return reduce(xor, map(hash, self.items()), 0)
# Alternative
def __hash__(self):
return sum(map(hash, self.items()))
一点:xor可以工作,部分原因是dict保证键是唯一的。sum可以工作,因为Python会按位截断结果。
如果你想散列一个多集,sum是更可取的。对于xor, {a}将哈希到与{a, a, a}相同的值,因为x ^ x ^ x = x。
如果您确实需要SHA提供的保证,那么这并不适合您。但是在集合中使用字典,这将很好;Python容器对某些冲突具有弹性,底层哈希函数非常好。
其他回答
我是这样做的:
hash(str(my_dict))
使用sorted(d.s items())并不足以获得稳定的repr。d中的一些值也可以是字典,它们的键仍然会以任意顺序出现。只要所有的键都是字符串,我更喜欢使用:
json.dumps(d, sort_keys=True)
也就是说,如果散列需要在不同的机器或Python版本之间保持稳定,我不确定这是万无一失的。您可能希望添加分隔符和ensure_ascii参数,以保护自己不受对默认值的任何更改的影响。我很感激你的评论。
如果你的字典不是嵌套的,你可以用字典的项创建一个frozenset,并使用hash():
hash(frozenset(my_dict.items()))
与生成JSON字符串或字典表示相比,这需要的计算量要小得多。
更新:请参阅下面的评论,为什么这种方法可能不会产生稳定的结果。
使用DeepDiff模块中的DeepHash
from deepdiff import DeepHash
obj = {'a':'1',b:'2'}
hashes = DeepHash(obj)[obj]
这里有一个更清晰的解决方案。
def freeze(o):
if isinstance(o,dict):
return frozenset({ k:freeze(v) for k,v in o.items()}.items())
if isinstance(o,list):
return tuple([freeze(v) for v in o])
return o
def make_hash(o):
"""
makes a hash out of anything that contains only list,dict and hashable types including string and numeric types
"""
return hash(freeze(o))