2009-10-22 9 views
49

मेरे प्रोग्राम के पायथन 3.1 कांटा में एक सुविधा पोर्ट करते समय मुझे एक अजीब बग था। मैंने इसे निम्नलिखित परिकल्पनाओं तक सीमित कर दिया:'__eq__` को परिभाषित करने वाले प्रकार अचूक हैं?

पायथन 2.x के विपरीत, पायथन 3.x में यदि किसी ऑब्जेक्ट में __eq__ विधि है तो यह स्वचालित रूप से अप्राप्य है।

क्या यह सच है?

यहाँ क्या अजगर 3.1 में क्या होता है:

>>> class O(object): 
...  def __eq__(self, other): 
...   return 'whatever' 
... 
>>> o = O() 
>>> d = {o: 0} 
Traceback (most recent call last): 
    File "<pyshell#16>", line 1, in <module> 
    d = {o: 0} 
TypeError: unhashable type: 'O' 

अनुवर्ती सवाल है, कैसे मैं अपने निजी समस्या को हल करते हैं? मेरे पास एक ऑब्जेक्ट ChangeTracker है जो WeakKeyDictionary स्टोर करता है जो कई ऑब्जेक्ट्स को इंगित करता है, जो अतीत में एक निश्चित समय बिंदु पर उनके अचार डंप के प्रत्येक मूल्य के लिए देता है। जब भी कोई मौजूदा ऑब्जेक्ट चेक किया जाता है, तो परिवर्तन ट्रैकर कहता है कि उसका नया अचार अपने पुराने के समान है, इसलिए कह रहा है कि इस दौरान ऑब्जेक्ट बदल गया है या नहीं। समस्या यह है कि अब मैं यह भी जांच नहीं सकता कि दी गई वस्तु लाइब्रेरी में है या नहीं, क्योंकि यह वस्तु को अचूक होने के बारे में अपवाद उठाती है। (क्योंकि इसमें __eq__ विधि है।) मैं इसके आसपास कैसे काम कर सकता हूं?

+3

यदि आप '__hash__' विधि प्रदान करते हैं तो क्या होता है? – ndim

उत्तर

50

हाँ, यदि आप __eq__ परिभाषित करते हैं, डिफ़ॉल्ट __hash__ (यानी, स्मृति में वस्तु का पता hashing) गायब हो जाता। यह महत्वपूर्ण है क्योंकि हैशिंग को समानता के अनुरूप होना चाहिए: बराबर वस्तुओं को हश की आवश्यकता होती है।

समाधान सरल है: को __eq__ परिभाषित करने के साथ ही परिभाषित करें।

+0

मुझे वंशावली के लिए जोड़ने दो: मैंने '__hash__' को 'वापसी आईडी (स्वयं)' के रूप में परिभाषित किया है। –

+25

@ कूल-आरआर: '__hash __()' में 'id (self)' का उपयोग निश्चित रूप से गलत है जब तक कि आपने ऑब्जेक्ट पहचान (जो अर्थहीन लगता है) की तुलना करने के लिए '__eq __()' परिभाषित किया हो। असली '__eq __() 'कार्यान्वयन के साथ अपना प्रश्न अपडेट करने के बारे में क्या है ताकि हम इसका पूरक' __hash __()' सुझा सकें? –

+0

मैं इस मुद्दे को हल करने की कोशिश कर रहा हूं; मैं इसके साथ आया: 'def __eq __(): वापस लौटें .__ dict__ == अन्य .__ dict__ 'और' def __hash __(): वापसी हैश (स्वयं .__ dict __। मान()) ' –

1

मैं कोई अजगर विशेषज्ञ नहीं हूं, लेकिन यह समझ में नहीं आता है कि, जब आप एक ईक-विधि को परिभाषित करते हैं, तो आपको एक हैश-विधि भी परिभाषित करना होगा (जो वस्तु के लिए हैश मान की गणना करता है) अन्यथा , हैशिंग तंत्र को यह नहीं पता होगा कि क्या यह एक ही ऑब्जेक्ट, या एक ही ऑब्जेक्ट को एक ही हैश-वैल्यू के साथ दबाता है। दरअसल, यह दूसरी तरफ है, यह शायद आपके __eq__ विधि के बराबर मानी जाने वाली वस्तुओं के लिए विभिन्न हैश मानों की गणना करना समाप्त कर देगा।

मुझे नहीं पता कि हैश फ़ंक्शन को क्या कहा जाता है, __hash__ शायद? :)

+6

एफवाईआई: मैंने इसे कुछ समय पहले खींचा (http://www.mindmeister.com/10510492/python-underscore): अजगर में सभी अंडरस्कोर विधियों का एक दिमाग। – jldupont

+2

यह वास्तव में इसके विपरीत है। यदि दो वस्तुओं में एक ही हैश मान है, लेकिन बराबर नहीं हैं - यह ठीक है। "हैशिंग तंत्र" (यानी शब्दकोश) पहले हैश की जांच करता है, और, उसी हैश पर भी समानता की तुलना करता है। वास्तविक समस्या दूसरी तरफ 'राउंड: ऑब्जेक्ट हैश अलग-अलग होती है लेकिन फिर भी इसकी तुलना करती है। शब्दकोश उन्हें ढूंढना चाहिए, लेकिन नहीं (या आपको शब्दकोश में डुप्लिकेट कुंजी मिल सकती है)। –

+0

@ मार्टिन वी। लोविस: मुझे एहसास हुआ, और अंत में कहा, या क्या मैंने कहा है कि इसमें एक सूक्ष्म अंतर है? – falstro

3

चेक object.__hash__ पर पायथन 3 मैनुअल:

एक वर्ग एक __eq__() विधि यह एक __hash__() आपरेशन को परिभाषित नहीं किया जाना चाहिए परिभाषित नहीं करता है, तो या तो; यदि यह __eq__() को परिभाषित करता है लेकिन __hash__() नहीं है, तो इसके उदाहरण हैशबल संग्रह में आइटम के रूप में उपयोग योग्य नहीं होंगे।

जोर मेरा है।

आप आलसी होना चाहते हैं, ऐसा लगता है तुम सिर्फ id(self) वापस जाने के लिए __hash__(self) परिभाषित कर सकते हैं लगता है:

उपयोगकर्ता-परिभाषित वर्गों डिफ़ॉल्ट रूप से __eq__() और __hash__() तरीकों है; उनके साथ, सभी वस्तुएं असमान की तुलना करती हैं (स्वयं को छोड़कर) और x.__hash__()id(x) लौटाती है।

+3

निश्चित रूप से बराबर ऑपरेटर को अधिभारित करने का एकमात्र कारण यह है कि दो अलग-अलग ऑब्जेक्ट कभी-कभी बराबर तुलना कर सकते हैं (यदि नहीं तो इसे ओवरलोडिंग परेशान न करें)। इस मामले में 'वापसी आईडी (स्वयं)' हैश फ़ंक्शन टूटा हुआ है (समान वस्तुओं को हश होना चाहिए, मार्टिन का जवाब देखें)। "मुझे परवाह नहीं है" हैश फ़ंक्शन 'वापसी 1' होगा। यह सरल है और सभी शर्तों को पूरा करता है (यह बहुत ही अक्षम है!) –

17

http://docs.python.org/3.1/reference/datamodel.html#object.hash

से इस पैरा एक वर्ग है कि __eq__() जरूरतों को ओवरराइड करता है __hash__() एक माता पिता के वर्ग से के कार्यान्वयन बनाए रखने के लिए, दुभाषिया स्पष्ट __hash__ = <ParentClass>.__hash__ सेट करके ऐसा बताया जाना चाहिए। अन्यथा __hash__() की विरासत अवरुद्ध हो जाएगी, जैसे __hash__ स्पष्ट रूप से किसी के लिए सेट नहीं किया गया था।

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