Listeler değiştirilebilir olduğu için, dict
anahtarlar (ve set
üyeleri) ihtiyaç hashable olmak ve karma değerleri nedeniyle değişken nesneleri karma kötü bir fikir olmalıdır örneği davranışına göre hesaplanabilir.
Bu cevapta bazı somut örnekler vereceğim, umarım mevcut cevapların üstüne değer katar. Her içgörü, veri yapısının öğeleri için de geçerlidir set
.
Örnek 1 : Karma değerin nesnenin değiştirilebilir bir özelliğine dayandığı bir değiştirilebilir nesneye hashing uygulamak.
>>> 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
Mutasyona uğratıldıktan sonra stupid
, karma değiştiği için artık diktede bulunamaz. Yalnızca diktenin anahtarlarının listesi üzerinde doğrusal bir tarama bulunur stupid
.
Örnek 2 : ... ama neden sabit bir hash değeri olmasın?
>>> 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
Bu da iyi bir fikir değil çünkü eşit nesneler, onları a dict
veya içinde bulabileceğiniz şekilde özdeş olarak karma yapmalıdır set
.
Örnek 3 : ... tamam, peki ya tüm örneklerdeki sabit karmalar ?!
>>> 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
İşler beklendiği gibi çalışıyor gibi görünüyor, ancak neler olduğunu bir düşünün: Sınıfınızın tüm örnekleri aynı hash değerini ürettiğinde, a'da dict
veya a'da anahtar olarak ikiden fazla örnek olduğunda bir hash çarpışması yaşarsınız set
.
Doğru örneği my_dict[key]
veya key in my_dict
(veya item in my_set
) ile bulmak, stupidlist3
diktenin anahtarlarında (en kötü durumda) olduğu kadar çok eşitlik kontrolü gerçekleştirmelidir . Bu noktada, sözlüğün amacı - O (1) araması - tamamen bozulur. Bu, aşağıdaki zamanlamalarda gösterilmiştir (IPython ile yapılır).
Örnek 3 için Bazı Zamanlamalar
>>> 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)
Gördüğünüz gibi , bir sürü hash çarpışması olmayan bir sette beklenen süper hızlı arama süresine (faktör 500) sahip olurken , bizim üyelik testi stupidlists_set
bütün olarak doğrusal bir taramadan bile daha yavaştır lists_list
.
TL; DR: anahtarlar tuple(yourlist)
olarak kullanabilirsiniz dict
, çünkü başlıklar değişmez ve karıştırılabilirdir.