散列字典?
为了caching的目的,我需要从字典中的GET参数中生成一个caching键。
目前我正在使用sha1(repr(sorted(my_dict.items())))
( sha1()
是一个方便的方法,在内部使用hashlib),但我很好奇,如果有更好的方法。
如果你的字典没有嵌套,你可以使用字典的项目并使用hash()
来进行冻结设置:
hash(frozenset(my_dict.items()))
这比生成JSONstring或字典的表示要less得多的计算密集度。
使用sorted(d.items())
是不足以让我们一个稳定的repr。 d
一些值也可能是字典,而且它们的键仍然会以任意的顺序出现。 只要所有的键都是string,我宁愿使用:
json.dumps(d, sort_keys=True)
也就是说,如果哈希需要在不同的机器或Python版本中保持稳定,我不确定这是否是防弹的。 您可能需要添加separators
和ensure_ascii
参数,以防止对其中的默认值进行任何更改。 我会很感激评论。
编辑 :如果你所有的键都是string ,那么在继续阅读这个答案之前,请看杰克·奥康纳( Jack O'Connor)明显更简单(也更快)的解决scheme (也适用于散列嵌套字典)。
虽然答案已经被接受,但问题的标题是“散列python字典”,关于该标题的答案是不完整的。 (关于问题的主体,答案是完整的。)
嵌套字典
如果在堆栈溢出中search字典的哈希值,人们可能会偶然发现这个恰当的问题,如果试图散列多重嵌套的字典,就不会令人满意。 上面的答案在这种情况下不起作用,你将不得不实现某种recursion机制来检索哈希。
这是一个这样的机制:
import copy def make_hash(o): """ Makes a hash from a dictionary, list, tuple or set to any level, that contains only other hashable types (including any lists, tuples, sets, and dictionaries). """ if isinstance(o, (set, tuple, list)): return tuple([make_hash(e) for e in o]) elif not isinstance(o, dict): return hash(o) new_o = copy.deepcopy(o) for k, v in new_o.items(): new_o[k] = make_hash(v) return hash(tuple(frozenset(sorted(new_o.items()))))
奖金:哈希对象和类
hash()函数在散列类或实例时效果很好。 然而,这里有一个问题,我发现哈希,关于对象:
class Foo(object): pass foo = Foo() print (hash(foo)) # 1209812346789 foo.a = 1 print (hash(foo)) # 1209812346789
哈希是一样的,即使我改变了富。 这是因为foo的身份没有改变,所以hash是一样的。 如果你希望foo根据当前的定义进行不同的散列,解决方法是散列实际上正在改变的东西。 在这种情况下,__dict__属性:
class Foo(object): pass foo = Foo() print (make_hash(foo.__dict__)) # 1209812346789 foo.a = 1 print (make_hash(foo.__dict__)) # -78956430974785
唉,当你试图对class级本身做同样的事情时:
print (make_hash(Foo.__dict__)) # TypeError: unhashable type: 'dict_proxy'
类__dict__属性不是一个正常的字典:
print (type(Foo.__dict__)) # type <'dict_proxy'>
这里有一个和以前类似的机制,可以正确处理类:
import copy DictProxyType = type(object.__dict__) def make_hash(o): """ Makes a hash from a dictionary, list, tuple or set to any level, that contains only other hashable types (including any lists, tuples, sets, and dictionaries). In the case where other kinds of objects (like classes) need to be hashed, pass in a collection of object attributes that are pertinent. For example, a class can be hashed in this fashion: make_hash([cls.__dict__, cls.__name__]) A function can be hashed like so: make_hash([fn.__dict__, fn.__code__]) """ if type(o) == DictProxyType: o2 = {} for k, v in o.items(): if not k.startswith("__"): o2[k] = v o = o2 if isinstance(o, (set, tuple, list)): return tuple([make_hash(e) for e in o]) elif not isinstance(o, dict): return hash(o) new_o = copy.deepcopy(o) for k, v in new_o.items(): new_o[k] = make_hash(v) return hash(tuple(frozenset(sorted(new_o.items()))))
你可以使用它来返回你想要的许多元素的哈希元组:
# -7666086133114527897 print (make_hash(func.__code__)) # (-7666086133114527897, 3527539) print (make_hash([func.__code__, func.__dict__])) # (-7666086133114527897, 3527539, -509551383349783210) print (make_hash([func.__code__, func.__dict__, func.__name__]))
注意:所有上面的代码假定Python 3.x. 没有在早期版本中testing,但我认为make_hash()可以在2.7.2中工作。 至于做例子的工作,我知道这一点
func.__code__
应该换成
func.func_code
这是一个更清晰的解决scheme。
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(str(dictionary))
或hash(json.dumps(dictionary))
我宁愿快速和肮脏的解决scheme:
from pprint import pformat h = hash(pformat(dictionary))
它甚至可以用于像DateTime
这样的types,而不是JSON可序列化的types。
从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
下面的代码避免了使用Python hash()函数,因为它不会提供在Python重新启动时保持一致的散列 (请参阅Python 3.3中的散列函数返回会话之间的不同结果 )。 make_hashable()
会将对象转换为嵌套元组,而make_hash_sha256()
也会将repr()
转换为base64编码的SHA256散列。
import hashlib import base64 def make_hash_sha256(o): hasher = hashlib.sha256() hasher.update(repr(make_hashable(o)).encode()) return base64.b64encode(hasher.digest()).decode() def make_hashable(o): if isinstance(o, (tuple, list)): return tuple((make_hashable(e) for e in o)) if isinstance(o, dict): return tuple(sorted((k,make_hashable(v)) for k,v in o.items())) if isinstance(o, (set, frozenset)): return tuple(sorted(make_hashable(e) for e in o)) return o o = dict(x=1,b=2,c=[3,4,5],d={6,7}) print(make_hashable(o)) # (('b', 2), ('c', (3, 4, 5)), ('d', (6, 7)), ('x', 1)) print(make_hash_sha256(o)) # fyt/gK6D24H9Ugexw+g3lbqnKZ0JAcgtNW+rXIDeU2Y=
一般的方法是好的,但你可能要考虑哈希方法。
SHA被devise用于encryption强度(速度也是如此,但是强度更重要)。 您可能需要考虑这一点。 因此,使用内置的hash
函数可能是一个好主意,除非安全性在某种程度上是关键。
我这样做:
hash(str(my_dict))