为了缓存目的,我需要从字典中存在的GET参数生成一个缓存键。
目前,我正在使用sha1(repr(sorted(my_dict.items()))) (sha1()是一个内部使用hashlib的方便方法),但我很好奇是否有更好的方法。
为了缓存目的,我需要从字典中存在的GET参数生成一个缓存键。
目前,我正在使用sha1(repr(sorted(my_dict.items()))) (sha1()是一个内部使用hashlib的方便方法),但我很好奇是否有更好的方法。
当前回答
这里有一个更清晰的解决方案。
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))
其他回答
虽然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(tuple(my_dict.items()))
MD5哈希
对我来说,产生最稳定结果的方法是使用md5哈希和json.stringify
from typing import Dict, Any
import hashlib
import json
def dict_hash(dictionary: Dict[str, Any]) -> str:
"""MD5 hash of a dictionary."""
dhash = hashlib.md5()
# We need to sort arguments so {'a': 1, 'b': 2} is
# the same as {'b': 2, 'a': 1}
encoded = json.dumps(dictionary, sort_keys=True).encode()
dhash.update(encoded)
return dhash.hexdigest()
这里有一个更清晰的解决方案。
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))
更新自2013年回复…
以上答案在我看来都不可靠。原因是使用了items()。据我所知,这是一个依赖于机器的顺序。
这个怎么样?
import hashlib
def dict_hash(the_dict, *ignore):
if ignore: # Sometimes you don't care about some items
interesting = the_dict.copy()
for item in ignore:
if item in interesting:
interesting.pop(item)
the_dict = interesting
result = hashlib.sha1(
'%s' % sorted(the_dict.items())
).hexdigest()
return result