EDIT : Si toutes vos clés sont des chaînes , avant de continuer à lire cette réponse, veuillez consulter la solution nettement plus simple (et plus rapide) de Jack O'Connor (qui fonctionne également pour le hachage de dictionnaires imbriqués).
Bien qu'une réponse ait été acceptée, le titre de la question est "Hashing a python dictionary", et la réponse est incomplète en ce qui concerne ce titre. (En ce qui concerne le corps de la question, la réponse est complète.)
Dictionnaires imbriqués
Si l'on recherche Stack Overflow pour savoir comment hacher un dictionnaire, on peut tomber sur cette question bien intitulée, et ne pas être satisfait si l'on tente de hacher plusieurs dictionnaires imbriqués. La réponse ci-dessus ne fonctionnera pas dans ce cas, et vous devrez implémenter une sorte de mécanisme récursif pour récupérer le hachage.
Voici un de ces mécanismes:
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: Objets et classes de hachage
La hash()
fonction fonctionne très bien lorsque vous hachez des classes ou des instances. Cependant, voici un problème que j'ai trouvé avec le hachage, en ce qui concerne les objets:
class Foo(object): pass
foo = Foo()
print (hash(foo)) # 1209812346789
foo.a = 1
print (hash(foo)) # 1209812346789
Le hachage est le même, même après avoir modifié foo. C'est parce que l'identité de foo n'a pas changé, donc le hachage est le même. Si vous voulez que foo hache différemment en fonction de sa définition actuelle, la solution est de hacher tout ce qui change réellement. Dans ce cas, l' __dict__
attribut:
class Foo(object): pass
foo = Foo()
print (make_hash(foo.__dict__)) # 1209812346789
foo.a = 1
print (make_hash(foo.__dict__)) # -78956430974785
Hélas, lorsque vous essayez de faire la même chose avec la classe elle-même:
print (make_hash(Foo.__dict__)) # TypeError: unhashable type: 'dict_proxy'
La __dict__
propriété class n'est pas un dictionnaire normal:
print (type(Foo.__dict__)) # type <'dict_proxy'>
Voici un mécanisme similaire au précédent qui gérera les classes de manière appropriée:
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()))))
Vous pouvez l'utiliser pour renvoyer un tuple de hachage du nombre d'éléments que vous souhaitez:
# -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__]))
REMARQUE: tout le code ci-dessus suppose Python 3.x. N'a pas testé dans les versions antérieures, bien que je suppose que make_hash()
cela fonctionnera dans, disons, 2.7.2. En ce qui concerne les travaux faisant des exemples, je ne sais que
func.__code__
doit être remplacé par
func.func_code