2012-10-21 23 views
6

'u çağırdığınızda ne olur? __hash__ ve __eq__ öğelerini uygulayan bir sınıfa sahibim (buna diyelim). Aynı zamanda myClass nesnesini, biraz zaman alan bir değere çeviren bir dict ürünüm var.Eğer 'dict' anahtarında

Programım boyunca, birçok (milyonlarca) myClass nesnesi oluşturuldu. Bu yüzden bu değerleri takip etmek için dict kullanıyorum. Ancak, bazen yeni bir myClass nesnesi daha eski bir değere eşdeğer olabilir (__eq__ yöntemiyle tanımlandığı gibi). Bu nesnenin değerini tekrar hesaplamak yerine, dict'daki eski myClass nesnesinin değerini bulmayı tercih ederim. Bunu başarmak için if myNewMyClassObj in dict yapıyorum.

İşte sorum: denilen Nelerin

Ben in maddesi kullanan, __hash__ veya __eq__? dict kullanmanın amacı, O (1) arama süresinin olmasıdır. O zaman __hash__ aranmalıdır. Ancak, __hash__ ve __eq__ eşdeğer yöntemleri değilse ne olur? Bu durumda, if myNewMyClassObj in dict için yanlış bir pozitif mi alacağım?

soru Takip:

Ben benim dict girişlerin sayısını en aza indirmek istiyorum, bu yüzden ideal olarak dict eşdeğer myClass nesne kümeleri sadece bir tutmak istiyorum. Yani yine, if myNewClassObj in dict hesaplarken __eq__ ihtiyaçları dict 'ın O'yu kirletmek hangi, çağrılacak görünüyor (1) Bir O zaman arama (n) seferinde arama

cevap

8

İlk olarak, __hash__(myNewMyClassObj) aranıyor. Sözlüğünde aynı kargıya sahip hiçbir nesne bulunamazsa, Python sözlüğünde myNewMyClassObj'un bulunmadığını varsayar. (Python __eq__ iki nesne için eşit değerlendirir zaman, kendi __hash__ aynı olmalıdır gerektirdiğini not edin.)

aynı __hash__ bazı amaçları, sözlükte bulunan __eq__ her biri üzerinde çağrılan edin. __eq__ bunlardan herhangi biri için eşit olarak değerlendirirse, myNewMyClassObj in dict_, True değerini döndürür.

Bu nedenle, yalnızca __eq__ ve __hash__ öğelerinin hızlı olduğundan emin olmanız yeterlidir. sizin bir takip soru için

: evet, dict_ mağazalar (__eq__ tarafından tanımlandığı gibi) eşdeğer MyClass nesne kümeleri sadece biri. (Olduğu gibi.)

Not: __eq__ yalnızca aynı karıta sahip ve aynı kepçe için ayrılmış olan nesnelerde çağrılır. Bu gibi nesnelerin sayısı genellikle çok küçük bir sayıdır (dict uygulaması bunun yapılmasını sağlar). Yani hala (kabaca) O(1) arama performansınız var.

7
__hash__ daima adı verilecek

; Nesne gerçekten sözlükte ise veya aynı karta sahip başka bir nesne sözlükte ise __eq__ çağrılacaktır. Karma değer, olası tuşların seçimini daraltmak için kullanılır. Anahtarlar, hash değeriyle "kepçeler" olarak gruplandırılmıştır, ancak Python'un arama için hala, arama anahtarıyla eşitlik için kepçe içindeki her anahtarı kontrol etmesi gerekir. Bakınız http://wiki.python.org/moin/DictionaryKeys. Bu örneklere bakın:

>>> class Foo(object): 
...  def __init__(self, x): 
...   self.x = x 
...  
...  def __hash__(self): 
...   print "Hash" 
...   return hash(self.x) 
... 
...  def __eq__(self, other): 
...   print "Eq" 
...   return self.x == other.x 
>>> Foo(1) in d 
Hash 
Eq 
10: True 
>>> Foo(2) in d 
Hash 
Eq 
11: True 
>>> Foo(3) in d 
Hash 
Eq 
12: True 
>>> Foo(4) in d 
Hash 
13: False 

o örnekte, her zaman __hash__ denir görebilirsiniz. __eq__, her bir arama için nesne her zaman farklı karma değerleri içerdiğinden, her bir arama için bir kez çağrılır; bu nedenle, bu karma değere sahip nesnenin gerçekten sorgulandığı biri olduğunu doğrulamak için bir eşitlik denetimi yeterlidir. __eq__ son durumda çağrılmaz, çünkü dict içindeki nesnelerin hiçbiri Foo(4) ile aynı karma değere sahip değildir, bu nedenle Python'un __eq__ ile devam etmesi gerekmez.

>>> class Foo(object): 
...  def __init__(self, x): 
...   self.x = x 
...  
...  def __hash__(self): 
...   print "Hash" 
...   return 1 
... 
...  def __eq__(self, other): 
...   print "Eq" 
...   return self.x == other.x 
>>> d = {Foo(1): 2, Foo(2): 3, Foo(3): 4} 
Hash 
Hash 
Eq 
Hash 
Eq 
Eq 
>>> Foo(1) in d 
Hash 
Eq 
18: True 
>>> Foo(2) in d 
Hash 
Eq 
Eq 
19: True 
>>> Foo(3) in d 
Hash 
Eq 
Eq 
Eq 
20: True 
>>> Foo(4) in d 
Hash 
Eq 
Eq 
Eq 
21: False 

Bu sürümde, tüm nesneler aynı karma değere sahiptir. Bu durumda __eq__ her zaman, bazen birden çok kez çağrılır, çünkü hash değerleri birbirinden ayırmaz, bu nedenle Python eşitliği bir eşit olana kadar eşitlikteki tüm değerlere karşı eşitlikle kontrol etmelidir (ya da bunların hiçbiri eşit değildir). aradığı şey). Bazen ilk denemede (yukarıda Foo(1) in dict) bulur, bazen tüm değerleri kontrol etmek zorundadır.

+0

@MartijnPieters: Sadece kazara dahil edilmeden önce yanlışlıkla kazandım, şimdi oradalar. – BrenBarn

+0

Fantastik örnekler! – inspectorG4dget

+1

Python, karmaları karma tablolarında kullanmaz: her bir yuva, tek bir değer içeren yuvalar kullanır. Bir yuva doluysa, bir eşleşme veya kullanılmayan bir yuva bulana kadar başka bir yuva seçer. – Duncan

1

__hash__ nesnenin içine konduğunu tanımlar, __eq__ yalnızca nesneler aynı kovadayken çağrılır.