Cálculo de un hash md5 de una estructura de datos.

Quiero calcular un hash md5 no de una cadena, sino de una estructura de datos completa. Entiendo la mecánica de una manera de hacer esto (envío sobre el tipo de valor, canonicalizar el orden de las claves del diccionario y otras aleatoriedades, recuperar en subvalores, etc.). Pero parece ser el tipo de operación que sería útil en general, así que me sorprende que necesito hacer esto yo mismo.

¿Hay alguna forma más sencilla en Python para lograr esto?

ACTUALIZACIÓN: se ha sugerido pickle, y es una buena idea, pero pickling no canonicaliza el orden de las claves del diccionario:

>>> import cPickle as pickle >>> import hashlib, random >>> for i in range(10): ... k = [i*i for i in range(1000)] ... random.shuffle(k) ... d = dict.fromkeys(k, 1) ... p = pickle.dumps(d) ... print hashlib.md5(p).hexdigest() ... 51b5855799f6d574c722ef9e50c2622b 43d6b52b885f4ecb4b4be7ecdcfbb04e e7be0e6d923fe1b30c6fbd5dcd3c20b9 aebb2298be19908e523e86a3f3712207 7db3fe10dcdb70652f845b02b6557061 43945441efe82483ba65fda471d79254 8e4196468769333d170b6bb179b4aee0 951446fa44dba9a1a26e7df9083dcadf 06b09465917d3881707a4909f67451ae 386e3f08a3c1156edd1bd0f3862df481 

bencode ordena los diccionarios para:

 import hashlib import bencode data = ['only', 'lists', [1,2,3], 'dictionaries', {'a':0,'b':1}, 'numbers', 47, 'strings'] data_md5 = hashlib.md5(bencode.bencode(data)).hexdigest() print data_md5 

huellas dactilares:

 af1b88ca9fd8a3e828b40ed1b9a2cb20 

json.dumps () puede ordenar los diccionarios por clave. Entonces no necesitas otras dependencias:

 import hashlib import json data = ['only', 'lists', [1,2,3], 'dictionaries', {'a':0,'b':1}, 'numbers', 47, 'strings'] data_md5 = hashlib.md5(json.dumps(data, sort_keys=True)).hexdigest() print(data_md5) 

Huellas dactilares:

 87e83d90fc0d03f2c05631e2cd68ea02 

Terminé escribiéndolo yo mismo porque pensé que tendría que:

 class Hasher(object): """Hashes Python data into md5.""" def __init__(self): self.md5 = md5() def update(self, v): """Add `v` to the hash, recursively if needed.""" self.md5.update(str(type(v))) if isinstance(v, basestring): self.md5.update(v) elif isinstance(v, (int, long, float)): self.update(str(v)) elif isinstance(v, (tuple, list)): for e in v: self.update(e) elif isinstance(v, dict): keys = v.keys() for k in sorted(keys): self.update(k) self.update(v[k]) else: for k in dir(v): if k.startswith('__'): continue a = getattr(v, k) if inspect.isroutine(a): continue self.update(k) self.update(a) def digest(self): """Retrieve the digest of the hash.""" return self.md5.digest() 

ACTUALIZACIÓN: esto no funcionará para los diccionarios debido a la aleatoriedad del orden de las teclas. Lo siento, no he pensado en ello.

 import hashlib import cPickle as pickle data = ['anything', 'you', 'want'] data_pickle = pickle.dumps(data) data_md5 = hashlib.md5(data_pickle).hexdigest() 

Esto debería funcionar para cualquier estructura de datos de Python, y también para objetos.

Modo ROCKY: Coloque todos los elementos de su estructura en una entidad principal (si aún no lo ha hecho), repárelos y ordénelos / canonicalice / etc, luego calcule el md5 de su repr .

Si bien requiere una dependencia en joblib , he encontrado que joblib.hashing.hash(object) funciona muy bien y está diseñado para usarse con el mecanismo de almacenamiento en caché del disco de joblib . Empíricamente, parece estar produciendo resultados consistentes de una ejecución a otra, incluso en datos que se mezclan en diferentes ejecuciones.

Alternativamente, podría interesarle la función compute_fixed_hash artemis-ml , que teóricamente hashs los objetos de una manera que sea consistente en todas las ejecuciones. Sin embargo, no lo he probado yo mismo.

Perdón por publicar millones de años después de la pregunta original 😅