2011-08-03 23 views
11

Estoy pensando en crear una suma de comprobación de un diccionario para saber si ha sido modificado o no Por el momento tengo que:Python, suma de comprobación de un dict

>>> import hashlib 
>>> import pickle 
>>> d = {'k': 'v', 'k2': 'v2'} 
>>> z = pickle.dumps(d) 
>>> hashlib.md5(z).hexdigest() 
'8521955ed8c63c554744058c9888dc30' 

Tal vez existe una solución mejor?

Nota: Quiero crear una identificación única de un dict para crear un buen Etag.

EDIT: Puedo tener datos abstractos en el dict.

+0

¿Qué contiene tu dict? Si solo se trata de cadenas (por ejemplo), simplemente podría hash la representación de cadena ordenada: 'hash (repr (sorted (my_dict.items())))'. – katrielalex

+0

¿Qué es información abstracta? La estabilidad y la funcionalidad del algoritmo dict-hash depende en gran medida de los datos que contiene. Por ejemplo, ¿qué pasa si tienes un dict de dicts? – katrielalex

+0

estos tipos de datos: http://code.google.com/appengine/docs/python/datastore/typesandpropertyclasses.html – sahid

Respuesta

7

Algo como esto:

reduce(lambda x,y : x^y, [hash(item) for item in d.items()]) 

Tome el hash de cada tupla (clave, valor) en el dict y XOR en total.

@katrielalex Si el dict contiene artículos unhashable usted puede hacer esto:

hash(str(d)) 

o tal vez incluso mejor

hash(repr(d)) 
+0

Esto es elegante. –

+0

¿Qué pasa si el dict contiene elementos inhábiles? – katrielalex

+1

No puede hacer 'str (d)' sin falsos negativos, porque el orden en que aparecen los elementos en la representación de cadena no está definido. – katrielalex

1

No sé si pickle le garantiza que el hash se serializa de la misma manera todo el tiempo.

Si sólo dispone de diccionarios, me gustaría ir para o combinación de llamadas a keys(), sorted(), construir una cadena basada en los pares ordenados de clave/valor y calcular la suma de comprobación en que

+0

'" ".join ("% s,% s "% (x, y) para x, y en sorted (foo.iteritems())) '(donde foo es el dict) podría funcionar como una firma que puedes hash. –

+0

¿Y si tengo datos abstractos en mi dict? ¿eso no es un problema? – sahid

+0

Creo que entonces tendría que hacer una función recursiva que serializaría los datos ordenados para cada subestructura –

0

Como usted ha dicho, que quería para generar un eTag basado en el diccionario contenido, OrderedDict que conserva el orden del diccionario puede ser mejor candidato aquí. Solo itera a través de la clave, valora los pares y construye tu cadena Etag.

0

Creo que es posible que no se den cuenta de algunas de las sutilezas que entran en esto. El primer problema es que la implementación no define el orden en que los elementos aparecen en un dict. Esto significa que simplemente pidiendo str de un diccionario no funciona, ya que podría tener

str(d1) == "{'a':1, 'b':2}" 
str(d2) == "{'b':2, 'a':1}" 

y éstos se hash en diferentes valores. Si tiene elementos sólo hashable en el dict, puede hash de ellos y luego unirse a sus valores hash, como @Bart hace o simplemente

hash(tuple(sorted(hash(x) for x in d.items()))) 

Nota del sorted, porque hay que garantizar que la tupla hash sale en el mismo orden, independientemente del orden en que aparezcan los elementos en el dict. Si tiene dicts en el dict, podría recurse esto, pero será complicado.

pero sería fácil de romper cualquier implementación de este tipo si usted permite que datos arbitrarios en el diccionario, ya que simplemente puede escribir un objeto con un roto __hash__ implementación y el uso que. Y no puede usar id, porque entonces podría tener elementos iguales que se comparan diferentes.

La moraleja de la historia es que los dictados hash no son compatibles en Python por una razón.

0

En Python 3, la función hash se inicializa con un número aleatorio, que es diferente para cada sesión de python. Si eso no es aceptable para la aplicación prevista, use, p. zlib.adler32 para construir la suma de comprobación para un dict:

import zlib 

d={'key1':'value1','key2':'value2'} 
checksum=0 
for item in d.items(): 
    c1 = 1 
    for t in item: 
     c1 = zlib.adler32(bytes(repr(t),'utf-8'), c1) 
    checksum=checksum^c1 

print(checksum) 
Cuestiones relacionadas