Karena daftar dapat berubah, dict
kunci (dan set
anggota) harus dapat dicirikan, dan mencirikan objek yang dapat berubah adalah ide yang buruk karena nilai hash harus dihitung berdasarkan atribut instance.
Pada jawaban ini, saya akan memberikan beberapa contoh konkret, semoga menambah nilai di atas jawaban yang ada. Setiap wawasan berlaku untuk elemen struktur data set
juga.
Contoh 1 : hashing objek yang bisa berubah di mana nilai hash didasarkan pada karakteristik objek yang bisa berubah.
>>> class stupidlist(list):
... def __hash__(self):
... return len(self)
...
>>> stupid = stupidlist([1, 2, 3])
>>> d = {stupid: 0}
>>> stupid.append(4)
>>> stupid
[1, 2, 3, 4]
>>> d
{[1, 2, 3, 4]: 0}
>>> stupid in d
False
>>> stupid in d.keys()
False
>>> stupid in list(d.keys())
True
Setelah bermutasi stupid
, itu tidak dapat ditemukan lagi di dict karena hash berubah. Hanya pemindaian linier atas daftar kunci dict yang ditemukan stupid
.
Contoh 2 : ... tetapi mengapa tidak hanya nilai hash konstan?
>>> class stupidlist2(list):
... def __hash__(self):
... return id(self)
...
>>> stupidA = stupidlist2([1, 2, 3])
>>> stupidB = stupidlist2([1, 2, 3])
>>>
>>> stupidA == stupidB
True
>>> stupidA in {stupidB: 0}
False
Itu juga bukan ide yang bagus karena objek yang sama harus memiliki hash yang identik sehingga Anda dapat menemukannya di a dict
atau set
.
Contoh 3 : ... ok, bagaimana dengan hash konstan di semua instance ?!
>>> class stupidlist3(list):
... def __hash__(self):
... return 1
...
>>> stupidC = stupidlist3([1, 2, 3])
>>> stupidD = stupidlist3([1, 2, 3])
>>> stupidE = stupidlist3([1, 2, 3, 4])
>>>
>>> stupidC in {stupidD: 0}
True
>>> stupidC in {stupidE: 0}
False
>>> d = {stupidC: 0}
>>> stupidC.append(5)
>>> stupidC in d
True
Hal-hal tampaknya berfungsi seperti yang diharapkan, tetapi pikirkan tentang apa yang terjadi: ketika semua instance kelas Anda menghasilkan nilai hash yang sama, Anda akan mengalami benturan hash setiap kali ada lebih dari dua instance sebagai kunci dalam a dict
atau hadir di a set
.
Menemukan instance yang tepat dengan my_dict[key]
atau key in my_dict
(atau item in my_set
) perlu melakukan sebanyak mungkin pemeriksaan kesetaraan karena ada instance stupidlist3
di kunci dict (dalam kasus terburuk). Pada titik ini, tujuan pencarian kamus - O (1) - benar-benar dikalahkan. Ini ditunjukkan dalam pengaturan waktu berikut (dilakukan dengan IPython).
Beberapa Waktu untuk Contoh 3
>>> lists_list = [[i] for i in range(1000)]
>>> stupidlists_set = {stupidlist3([i]) for i in range(1000)}
>>> tuples_set = {(i,) for i in range(1000)}
>>> l = [999]
>>> s = stupidlist3([999])
>>> t = (999,)
>>>
>>> %timeit l in lists_list
25.5 µs ± 442 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit s in stupidlists_set
38.5 µs ± 61.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit t in tuples_set
77.6 ns ± 1.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Seperti yang Anda lihat, uji keanggotaan di kami stupidlists_set
bahkan lebih lambat daripada pemindaian linier secara keseluruhan lists_list
, sementara Anda memiliki waktu pencarian super cepat yang diharapkan (faktor 500) dalam satu set tanpa banyak benturan hash.
TL; DR: Anda dapat menggunakan tuple(yourlist)
sebagai dict
kunci, karena tupel tidak dapat diubah dan memiliki hash.