2011-08-03 38 views
11

Tôi đang nghĩ để tạo ra một checksum của một dict biết nếu nó đã được sửa đổi hay không Đối với thời điểm tôi có rằng:Python, checksum của một dict

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

Có lẽ một giải pháp tốt hơn tồn tại?

Lưu ý: Tôi muốn tạo một id duy nhất của một dict để tạo Etag tốt.

EDIT: Tôi có thể có dữ liệu trừu tượng trong dict.

+0

Dict của bạn chứa gì? Nếu nó chỉ là chuỗi (nói) bạn chỉ có thể băm đại diện chuỗi được sắp xếp: 'hash (repr (được sắp xếp (my_dict.items())))'. – katrielalex

+0

Dữ liệu trừu tượng là gì? Sự ổn định và làm việc của thuật toán băm dict phụ thuộc mạnh vào dữ liệu mà nó nắm giữ. Ví dụ, nếu bạn có một dict của dicts? – katrielalex

+0

các loại dữ liệu sau: http://code.google.com/appengine/docs/python/datastore/typesandpropertyclasses.html – sahid

Trả lời

7

Something như thế này:

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

Lấy giá trị băm của mỗi (khóa, giá trị) tuple trong dict và XOR chúng hoàn toàn.

@katrielalex Nếu dict chứa mục unhashable bạn có thể làm điều này:

hash(str(d)) 

hoặc thậm chí tốt hơn

hash(repr(d)) 
+0

Điều này là thanh lịch. –

+0

Điều gì sẽ xảy ra nếu dict chứa các mục không thể sửa được? – katrielalex

+1

Bạn không thể làm 'str (d)' mà không có âm bản sai, bởi vì thứ tự các mục xuất hiện trong biểu diễn chuỗi không được xác định. – katrielalex

1

Tôi không biết liệu pickle có đảm bảo rằng mã băm được tuần tự theo cùng một cách mọi lúc.

Nếu bạn chỉ có từ điển, tôi sẽ đi làm nước giải khát kết hợp của các cuộc gọi đến keys(), sorted(), xây dựng một chuỗi dựa trên chìa khóa cặp được sắp xếp/giá trị và tính toán checksum trên đó

+0

'" ".join ("% s,% s "% (x, y) cho x, y trong sắp xếp (foo.iteritems())) '(trong đó foo là dict) có thể hoạt động như một chữ ký mà bạn có thể băm. –

+0

Và nếu tôi có dữ liệu trừu tượng trong dict thì sao? đó không phải là một vấn đề? – sahid

+0

tôi nghĩ rằng sau đó bạn sẽ phải làm một chức năng đệ quy sẽ serialize các dữ liệu được sắp xếp cho mỗi cấu trúc phụ –

0

Như bạn nói, bạn muốn tạo ra một ETag dựa trên từ điển nội dung, OrderedDict giữ nguyên thứ tự của từ điển có thể là ứng cử viên tốt hơn ở đây. Chỉ cần lặp qua các cặp khóa, giá trị và xây dựng chuỗi Etag của bạn.

0

Tôi nghĩ bạn có thể không nhận ra một số sự tinh tế đi sâu vào vấn đề này. Vấn đề đầu tiên là thứ tự các mục xuất hiện trong một dict không được xác định bởi việc thực hiện. Điều này có nghĩa rằng chỉ đơn giản là yêu cầu str của một dict không làm việc, bởi vì bạn có thể có

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

và chúng sẽ băm để giá trị khác nhau. Nếu bạn có các mục chỉ hashable trong dict, bạn có thể băm họ và sau đó tham gia lên băm của họ, như @Bart làm hoặc chỉ đơn giản là

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

Lưu ý sorted, bởi vì bạn phải đảm bảo rằng các tuple băm đi ra trong cùng một thứ tự bất kể thứ tự các mục xuất hiện trong dict. Nếu bạn có dicts trong dict, bạn có thể recurse này, nhưng nó sẽ được phức tạp.

NHƯNG sẽ dễ dàng thực hiện bất kỳ việc thực hiện nào như thế này nếu bạn cho phép tùy ý dữ liệu trong từ điển, vì bạn có thể chỉ cần viết một đối tượng bị hỏng __hash__ triển khai và sử dụng. Và bạn không thể sử dụng id, bởi vì sau đó bạn có thể có các mục bằng nhau so sánh khác nhau.

Yếu tố đạo đức của câu chuyện là các băm băm không được hỗ trợ trong Python vì một lý do.

0

Trong Python 3, hàm băm được khởi tạo với một số ngẫu nhiên, khác với mỗi phiên python. Nếu điều đó không được chấp nhận cho ứng dụng dự định, hãy sử dụng ví dụ: zlib.adler32 để tạo tổng kiểm tra cho một 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) 
Các vấn đề liên quan