为了缓存目的,我需要从字典中存在的GET参数生成一个缓存键。

目前,我正在使用sha1(repr(sorted(my_dict.items()))) (sha1()是一个内部使用hashlib的方便方法),但我很好奇是否有更好的方法。


当前回答

为了保持键顺序,而不是哈希(str(字典))或哈希(json.dumps(字典)),我更喜欢快速和肮脏的解决方案:

from pprint import pformat
h = hash(pformat(dictionary))

它甚至可以用于DateTime等不能序列化的JSON类型。

其他回答

如果你的字典不是嵌套的,你可以用字典的项创建一个frozenset,并使用hash():

hash(frozenset(my_dict.items()))

与生成JSON字符串或字典表示相比,这需要的计算量要小得多。

更新:请参阅下面的评论,为什么这种方法可能不会产生稳定的结果。

使用sorted(d.s items())并不足以获得稳定的repr。d中的一些值也可以是字典,它们的键仍然会以任意顺序出现。只要所有的键都是字符串,我更喜欢使用:

json.dumps(d, sort_keys=True)

也就是说,如果散列需要在不同的机器或Python版本之间保持稳定,我不确定这是万无一失的。您可能希望添加分隔符和ensure_ascii参数,以保护自己不受对默认值的任何更改的影响。我很感激你的评论。

这不是一个通用的解决方案(即,如果你的字典不是嵌套的,它只是微不足道的工作),但由于这里没有人建议它,我认为分享它可能是有用的。

我们可以使用(第三方)不可变包创建一个字典的不可变“快照”,如下所示:

from immutables import Map

map = dict(a=1, b=2)
immap = Map(map)
hash(immap)

这似乎比原始字典的字符串化要快。

我是从一篇不错的文章中学到的。

虽然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()))