Untuk tujuan caching saya perlu membuat kunci cache dari argumen GET yang ada dalam dikt.
Saat ini saya menggunakan sha1(repr(sorted(my_dict.items())))
( sha1()
adalah metode kenyamanan yang menggunakan hashlib secara internal) tapi saya ingin tahu apakah ada cara yang lebih baik.
python
hash
dictionary
Pencuri
sumber
sumber
{'a': 1, 'b':2}
secara semantik sama dengan{'b':2, 'a':1}
). Saya belum menggunakannya pada sesuatu yang terlalu rumit namun YMMV tetapi umpan balik diterima.Jawaban:
Jika kamus Anda tidak bersarang, Anda bisa membuat frozenset dengan item dict dan menggunakan
hash()
:Ini jauh lebih sedikit komputasi daripada menghasilkan string JSON atau representasi kamus.
UPDATE: Silakan lihat komentar di bawah ini, mengapa pendekatan ini mungkin tidak menghasilkan hasil yang stabil.
sumber
hash()
fungsi tidak menghasilkan output yang stabil. Ini berarti bahwa, dengan input yang sama, ia mengembalikan hasil yang berbeda dengan contoh yang berbeda dari juru bahasa python yang sama. Bagi saya, sepertinya semacam nilai seed dihasilkan setiap kali penerjemah dimulai.Menggunakan
sorted(d.items())
tidak cukup untuk membuat kita repr stabil. Beberapa nilai dalamd
bisa berupa kamus juga, dan kunci-kuncinya masih akan keluar dalam urutan yang sewenang-wenang. Selama semua kunci adalah string, saya lebih suka menggunakan:Yang mengatakan, jika hash harus stabil di berbagai mesin atau versi Python, saya tidak yakin ini anti peluru. Anda mungkin ingin menambahkan
separators
danensure_ascii
argumen untuk melindungi diri dari perubahan apa pun ke default di sana. Saya menghargai komentar.sumber
ensure_ascii
Argumen akan melindungi terhadap masalah yang sama sekali hipotetis ini.make_hash
. gist.github.com/charlax/b8731de51d2ea86c6eb9default=str
kedumps
perintah. Tampaknya bekerja dengan baik.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:
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: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:Sayangnya, ketika Anda mencoba melakukan hal yang sama dengan kelas itu sendiri:
__dict__
Properti kelas bukan kamus normal:Berikut adalah mekanisme yang sama seperti sebelumnya yang akan menangani kelas dengan tepat:
Anda dapat menggunakan ini untuk mengembalikan hash tuple dari banyak elemen yang Anda inginkan:
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 bahwaharus diganti dengan
sumber
hash
sekitar daftar dan tupel. Kalau tidak, ini mengambil daftar bilangan bulat saya yang kebetulan menjadi nilai dalam kamus saya, dan mengembalikan daftar hash, yang bukan yang saya inginkan.Ini solusi yang lebih jelas.
sumber
if isinstance(o,list):
keif isinstance(obj, (set, tuple, list)):
maka fungsi ini dapat bekerja pada objek apapun.Kode di bawah ini menghindari penggunaan fungsi hash () Python karena tidak akan memberikan hash yang konsisten di me-restart Python (lihat fungsi hash di Python 3.3 mengembalikan hasil yang berbeda antara sesi ).
make_hashable()
akan mengkonversi objek ke tuple bersarang danmake_hash_sha256()
juga akan mengkonversirepr()
ke hash SHA256 yang disandikan base64.sumber
make_hash_sha256(((0,1),(2,3)))==make_hash_sha256({0:1,2:3})==make_hash_sha256({2:3,0:1})!=make_hash_sha256(((2,3),(0,1)))
. Ini bukan solusi yang saya cari, tapi ini perantara yang bagus. Saya sedang berpikir untuk menambahtype(o).__name__
awal masing-masing tuple untuk memaksa diferensiasi.tuple(sorted((make_hashable(e) for e in o)))
Diperbarui dari 2013 balasan ...
Tidak ada jawaban di atas yang tampaknya dapat diandalkan bagi saya. Alasannya adalah penggunaan item (). Sejauh yang saya tahu, ini keluar dalam urutan yang tergantung pada mesin.
Bagaimana dengan ini?
sumber
dict.items
tidak mengembalikan daftar yang dapat diperkirakan?frozenset
mengurus ituhash
bawaan tidak peduli tentang bagaimana konten frozenset dicetak atau sesuatu seperti itu. Uji di beberapa mesin dan versi python dan Anda akan melihatnya.Untuk mempertahankan pesanan utama, alih-alih
hash(str(dictionary))
atauhash(json.dumps(dictionary))
saya lebih suka solusi cepat dan kotor:Ini akan bekerja bahkan untuk tipe suka
DateTime
dan banyak lagi yang bukan JSON serializable.sumber
Anda bisa menggunakan
frozendict
modul pihak ketiga untuk membekukan dict Anda dan membuatnya menjadi hashable.Untuk menangani objek bersarang, Anda bisa menggunakan:
Jika Anda ingin mendukung lebih banyak jenis, gunakan
functools.singledispatch
(Python 3.7):sumber
dict
dariDataFrame
objek.elif
klausa berikut untuk membuatnya bekerja denganDataFrame
s:elif isinstance(x, pd.DataFrame): return make_hashable(hash_pandas_object(x).tolist())
Saya akan mengedit jawaban dan melihat apakah Anda menerimanya ...hash
pengacakan adalah fitur keamanan yang disengaja diaktifkan secara default di python 3.7.Anda dapat menggunakan perpustakaan peta untuk melakukan ini. Khususnya, maps.FrozenMap
Untuk menginstal
maps
, lakukan saja:Ini menangani
dict
kasus bersarang juga:Penafian: Saya adalah penulis
maps
perpustakaan.sumber
.recurse
. Lihat maps.readthedocs.io/en/latest/api.html#maps.FrozenMap.recurse . Memesan dalam daftar secara semantik bermakna, jika Anda menginginkan kemandirian pesanan, Anda dapat mengonversi daftar Anda menjadi set sebelum melakukan panggilan.recurse
. Anda juga dapat menggunakanlist_fn
parameter untuk.recurse
menggunakan struktur data hashable yang berbeda darituple
(.egfrozenset
)Salah satu cara untuk mendekati masalah adalah dengan membuat tuple dari item kamus:
sumber
Saya melakukannya seperti ini:
sumber
hash(str({'a': 1, 'b': 2})) != hash(str({'b': 2, 'a': 1}))
(sementara itu mungkin bekerja untuk beberapa kamus, kamus ini tidak dijamin berfungsi pada semua).