BEARBEITEN : Wenn alle Ihre Schlüssel Zeichenfolgen sind , lesen Sie bitte Jack O'Connors wesentlich einfachere (und schnellere) Lösung (die auch für das Hashing verschachtelter Wörterbücher funktioniert ) , bevor Sie diese Antwort weiter lesen .
Obwohl eine Antwort akzeptiert wurde, lautet der Titel der Frage "Hashing a Python Dictionary", und die Antwort ist in Bezug auf diesen Titel unvollständig. (In Bezug auf den Hauptteil der Frage ist die Antwort vollständig.)
Verschachtelte Wörterbücher
Wenn man im Stapelüberlauf nach dem Hash eines Wörterbuchs sucht, kann man auf diese treffend betitelte Frage stoßen und unbefriedigt bleiben, wenn man versucht, verschachtelte Wörterbücher zu hashen. Die obige Antwort funktioniert in diesem Fall nicht und Sie müssen einen rekursiven Mechanismus implementieren, um den Hash abzurufen.
Hier ist ein solcher Mechanismus:
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()))))
Bonus: Objekte und Klassen zerschlagen
Die hash()
Funktion funktioniert hervorragend, wenn Sie Klassen oder Instanzen hashen. Hier ist jedoch ein Problem, das ich mit Hash in Bezug auf Objekte gefunden habe:
class Foo(object): pass
foo = Foo()
print (hash(foo)) # 1209812346789
foo.a = 1
print (hash(foo)) # 1209812346789
Der Hash ist der gleiche, auch nachdem ich foo geändert habe. Dies liegt daran, dass sich die Identität von foo nicht geändert hat, sodass der Hash derselbe ist. Wenn Sie möchten, dass foo je nach aktueller Definition unterschiedlich hasht, besteht die Lösung darin, alles zu hashen, was sich tatsächlich ändert. In diesem Fall ist das __dict__
Attribut:
class Foo(object): pass
foo = Foo()
print (make_hash(foo.__dict__)) # 1209812346789
foo.a = 1
print (make_hash(foo.__dict__)) # -78956430974785
Leider, wenn Sie versuchen, dasselbe mit der Klasse selbst zu tun:
print (make_hash(Foo.__dict__)) # TypeError: unhashable type: 'dict_proxy'
Die class __dict__
-Eigenschaft ist kein normales Wörterbuch:
print (type(Foo.__dict__)) # type <'dict_proxy'>
Hier ist ein ähnlicher Mechanismus wie zuvor, der Klassen angemessen behandelt:
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()))))
Sie können dies verwenden, um ein Hash-Tupel mit beliebig vielen Elementen zurückzugeben:
# -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__]))
HINWEIS: Der gesamte obige Code setzt Python 3.x voraus. In früheren Versionen nicht getestet, obwohl ich davon make_hash()
ausgehe, dass dies beispielsweise in 2.7.2 funktioniert. Soweit die Beispiele funktionieren, weiß ich das
func.__code__
sollte durch ersetzt werden
func.func_code