EDIT : Jika semua kunci Anda adalah string , maka sebelum melanjutkan untuk membaca jawaban ini, silakan lihat solusi Jack O'Connor yang secara signifikan lebih sederhana (dan lebih cepat) (yang juga berfungsi untuk membuat kamus bersarang bersarang).
Meskipun jawaban telah diterima, judul pertanyaannya adalah "Hashing a python dictionary", dan jawabannya tidak lengkap sehubungan dengan judul itu. (Sehubungan dengan tubuh pertanyaan, jawabannya sudah lengkap.)
Kamus bersarang
Jika seseorang mencari Stack Overflow untuk bagaimana mem-hash kamus, orang mungkin akan tersandung pada pertanyaan yang berjudul tepat ini, dan membiarkannya tidak puas jika seseorang mencoba untuk meng hash kamus berlipat ganda yang bersarang. Jawaban di atas tidak akan berfungsi dalam kasus ini, dan Anda harus menerapkan semacam mekanisme rekursif untuk mengambil hash.
Berikut ini salah satu mekanismenya:
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: Objek dan Kelas Hashing
The hash()
fungsi bekerja besar ketika Anda hash kelas atau contoh. Namun, berikut adalah satu masalah yang saya temukan dengan hash, mengenai objek:
class Foo(object): pass
foo = Foo()
print (hash(foo)) # 1209812346789
foo.a = 1
print (hash(foo)) # 1209812346789
Hash sama, bahkan setelah saya mengubah foo. Ini karena identitas foo tidak berubah, jadi hashnya sama. Jika Anda ingin hash berbeda tergantung pada definisi saat ini, solusinya adalah dengan memotong apa pun yang sebenarnya berubah. Dalam hal ini, __dict__
atributnya:
class Foo(object): pass
foo = Foo()
print (make_hash(foo.__dict__)) # 1209812346789
foo.a = 1
print (make_hash(foo.__dict__)) # -78956430974785
Sayangnya, ketika Anda mencoba melakukan hal yang sama dengan kelas itu sendiri:
print (make_hash(Foo.__dict__)) # TypeError: unhashable type: 'dict_proxy'
__dict__
Properti kelas bukan kamus normal:
print (type(Foo.__dict__)) # type <'dict_proxy'>
Berikut adalah mekanisme yang sama seperti sebelumnya yang akan menangani kelas dengan tepat:
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()))))
Anda dapat menggunakan ini untuk mengembalikan hash tuple dari banyak elemen yang Anda inginkan:
# -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__]))
CATATAN: semua kode di atas mengasumsikan Python 3.x. Tidak menguji di versi sebelumnya, meskipun saya berasumsi make_hash()
akan bekerja, katakanlah, 2.7.2. Sejauh membuat contoh kerja, saya tidak tahu bahwa
func.__code__
harus diganti dengan
func.func_code