编辑:如果你所有的键都是字符串,那么在继续阅读这个答案之前,请参阅 Jack O'Connor 的显着simpler (and faster) solution(它也适用于散列嵌套字典)。
虽然答案已被接受,但问题的标题是“Hashing a python dictionary”,并且该标题的答案不完整。 (关于问题的主体,答案是完整的。)
嵌套字典
如果您在 Stack Overflow 上搜索如何对字典进行哈希处理,您可能会偶然发现这个标题恰如其分的问题,如果您尝试哈希处理多个嵌套字典,就会感到不满意。上面的答案在这种情况下不起作用,您必须实现某种递归机制来检索哈希。
这是一种这样的机制:
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.这是因为 foo 的身份没有改变,所以哈希是一样的。如果您希望 foo 根据其当前定义进行不同的散列,则解决方案是散列实际更改的任何内容。在这种情况下,__dict__ 属性:
class Foo(object): pass
foo = Foo()
print (make_hash(foo.__dict__)) # 1209812346789
foo.a = 1
print (make_hash(foo.__dict__)) # -78956430974785
唉,当你尝试对类本身做同样的事情时:
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。没有在早期版本中进行测试,尽管我假设 make_hash() 可以在 2.7.2 中工作。就使示例起作用而言,我确实知道
func.__code__
应该替换为
func.func_code