2012-10-21 16 views
6

पर कॉल करते हैं तो मेरे पास कक्षा है (चलो इसे myClass पर कॉल करें) जो __hash__ और __eq__ दोनों लागू करता है। मेरे पास dict भी है जो कुछ मूल्यों के लिए myClass ऑब्जेक्ट्स को मानचित्र करता है, कंप्यूटिंग जिसमें कुछ समय लगता है।क्या होता है जब आप 'अगर कुंजी में कुंजी'

मेरे कार्यक्रम के दौरान, कई (लाखों के क्रम में) myClass ऑब्जेक्ट्स तत्काल हैं। यही कारण है कि मैं उन मानों का ट्रैक रखने के लिए dict का उपयोग करता हूं।

हालांकि, कभी-कभी एक नया myClass ऑब्जेक्ट पुराने के बराबर हो सकता है (जैसा कि __eq__ विधि द्वारा परिभाषित किया गया है)। तो उस ऑब्जेक्ट के मान को फिर से गणना करने के बजाय, मैं बस dict में पुराने myClass ऑब्जेक्ट के मान को देखना चाहूंगा। इसे पूरा करने के लिए, मैं if myNewMyClassObj in dict करता हूं।

यहाँ मेरे सवाल है:

मुझे लगता है कि in खंड का उपयोग करते हैं, क्या कहा जाता हो जाता है, __hash__ या __eq__? dict का उपयोग करने का बिंदु यह है कि यह ओ (1) लुकअप समय है। तो __hash__ कहा जाना चाहिए। लेकिन क्या होगा यदि __hash__ और __eq__ समकक्ष विधियां नहीं हैं? उस स्थिति में, क्या मुझे if myNewMyClassObj in dict के लिए झूठी सकारात्मक मिलेगी?

सवाल को का पालन करें:

मैं अपने dict में प्रविष्टियों की संख्या कम करना चाहते हैं, तो मैं आदर्श रूप dict में बराबर myClass वस्तुओं का एक सेट का केवल एक ही रखना चाहते हैं। तो फिर, ऐसा लगता है कि जब if myNewClassObj in dict कंप्यूटिंग के नाम से जाना __eq__ जरूरत है, जो एक dict की हे अशुद्ध हैं (1) एक हे करने के लिए समय देखने (एन) समय देखने

उत्तर

8

पहला, __hash__(myNewMyClassObj) कॉल किया जाता है। यदि शब्दकोश में एक ही हैश वाला कोई ऑब्जेक्ट नहीं मिलता है, तो पाइथन मानता है कि myNewMyClassObj शब्दकोश में नहीं है। (ध्यान दें कि अजगर की आवश्यकता है कि जब भी __eq__ दो वस्तुओं के लिए के रूप में बराबर का मूल्यांकन करता है, उनके __hash__ समान होने चाहिए।)

ही __hash__ साथ कुछ वस्तुओं, शब्दकोश में पाए जाते हैं __eq__ इनमें से प्रत्येक पर बुलाया जाता है। यदि __eq__ उनमें से किसी के बराबर मूल्यांकन करता है, तो myNewMyClassObj in dict_ सत्य लौटाता है।

इस प्रकार, आपको बस यह सुनिश्चित करना होगा कि __eq__ और __hash__ दोनों तेज़ हैं।

आपके अनुवर्ती प्रश्न के लिए: हाँ, dict_ समकक्ष MyClass ऑब्जेक्ट्स के सेट में से केवल एक स्टोर को स्टोर करता है (जैसा कि __eq__ द्वारा परिभाषित किया गया है)। (जैसा कि सेट करता है।)

ध्यान दें कि __eq__ केवल उन वस्तुओं पर बुलाया जाता है जिनके पास एक ही हैश था और उसी बाल्टी को आवंटित किया गया था। ऐसी वस्तुओं की संख्या आमतौर पर बहुत छोटी संख्या होती है (dict कार्यान्वयन इसके बारे में सुनिश्चित करता है)। तो आपके पास अभी भी (लगभग) O(1) लुकअप प्रदर्शन है।

7

__hash__ हमेशा कहा जाएगा; __eq__ को कॉल किया जाएगा यदि ऑब्जेक्ट वास्तव में शब्दकोश में है, या यदि एक ही हैश वाला कोई अन्य ऑब्जेक्ट शब्दकोश में है। हैश मान का उपयोग संभावित कुंजी की पसंद को कम करने के लिए किया जाता है। कुंजी को हैश मान द्वारा "बाल्टी" में समूहीकृत किया जाता है, लेकिन लुकअप के लिए पाइथन को लुकअप कुंजी के साथ समानता के लिए बाल्टी में प्रत्येक कुंजी को अभी भी जांचना होता है। http://wiki.python.org/moin/DictionaryKeys देखें। इन उदाहरणों को देखो:

>>> 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 

कि उदाहरण में, आप देख सकते हैं __hash__ हमेशा कहा जाता है। __eq__ ऑब्जेक्ट में प्रत्येक ऑब्जेक्ट के लिए एक बार कॉल किया जाता है, क्योंकि उनके पास विशिष्ट हैश मान होते हैं, इसलिए एक समानता जांच यह सत्यापित करने के लिए पर्याप्त है कि उस हैश मान वाले ऑब्जेक्ट को वास्तव में पूछताछ की जा रही है। __eq__ को अंतिम मामले में नहीं कहा जाता है, क्योंकि dict में किसी भी ऑब्जेक्ट में Foo(4) के समान हैश मान नहीं है, इसलिए पायथन को __eq__ के साथ जारी रखने की आवश्यकता नहीं है।

>>> 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 

इस संस्करण में, सभी ऑब्जेक्ट्स में एक ही हैश मान है। इस मामले में __eq__ को हमेशा कई बार बुलाया जाता है, क्योंकि हैश मानों के बीच अंतर नहीं करता है, इसलिए पायथन को स्पष्ट रूप से तब तक सभी मूल्यों के विरुद्ध समानता की जांच करने की आवश्यकता होती है जब तक कि यह बराबर न हो (या पाते हैं कि उनमें से कोई भी बराबर नहीं है वह जिसकी तलाश में है)। कभी-कभी इसे पहली कोशिश (Foo(1) in dict उपरोक्त) पर मिलता है, कभी-कभी इसे सभी मानों की जांच करनी पड़ती है।

+0

@MartijnPieters: मैं बस उन्हें शामिल करने से पहले गलती से बचाता हूं, वे अब वहां हैं। – BrenBarn

+0

शानदार उदाहरण! – inspectorG4dget

+1

पायथन अपनी हैश तालिकाओं में बाल्टी का उपयोग नहीं करता है: यह प्रत्येक स्लॉट के साथ स्लॉट का उपयोग करता है जिसमें एकल मान होता है। यदि एक स्लॉट भरा हुआ है तो यह एक और स्लॉट का चयन करेगा और तब तक जब तक कोई मैच या अप्रयुक्त स्लॉट न मिले। – Duncan

1

__hash__ ऑब्जेक्ट को डालने वाली बाल्टी को परिभाषित करता है, __eq__ केवल तब कॉल किया जाता है जब ऑब्जेक्ट एक ही बाल्टी में होते हैं।

संबंधित मुद्दे