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

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


当前回答

解决这个问题的一种方法是用字典的元素创建一个元组:

hash(tuple(my_dict.items()))

其他回答

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

hash(frozenset(my_dict.items()))

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

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

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

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

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

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

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