2012-12-06 11 views
6

मैं शब्दकोशों की एक जोड़ी की तुलना करना चाहते हैं और 'अस्पष्ट' चल बिन्दु तुलना या बेहतर अभी तक numpy.allclose() का उपयोग ऐसा करने के लिए इस्तेमाल करते हैं। हालांकि, डाइक्ट्स के लिए पाइथन में डिफ़ॉल्ट == या != का उपयोग करके ऐसा नहीं होता है।तुलना अजगर dicts शामिल

मैं अगर वहाँ एक रास्ता चल बिन्दु तुलना आपरेशन बदलने के लिए (शायद सुरक्षित सफाई के लिए एक संदर्भ प्रबंधक का उपयोग कर) था सोच रहा था।

मेरा मानना ​​है कि यहां एक उदाहरण में मदद मिलेगी। मेरे पास एक गहरा घोंसला वाला नियम है जिसमें सभी प्रकार के मूल्य शामिल हैं। इनमें से कुछ मान फ़्लोटिंग पॉइंट मान हैं। मुझे पता है कि चल बिन्दु मूल्यों 'की तुलना' के लिए नुकसान की टन कर रहे हैं, आदि

d1 = {'a': {'b': 1.123456}} 
d2 = {'a': {'b': 1.1234578}} 

मैं != का उपयोग इन दो dicts तुलना करने के लिए और इसे वापस True अंतर केवल एक के भीतर चल रहे हैं बिन्दु संख्या करना चाहते हैं कुछ सीमा उदाहरण के लिए, बंद (सुनिश्चित करें कि अभी तक सटीक मुझे अभी तक सुनिश्चित नहीं है) मानों को अलग मत मानें।

मुझे लगता है कि मैं खुद को डिक्ट्स के माध्यम से फिर से चला सकता हूं और मैन्युअल रूप से numpy.allclose() फ्लोटिंग पॉइंट मानों के लिए उपयोग कर सकता हूं और अन्य सभी प्रकारों के लिए सामान्य समानता परीक्षण पर वापस आ सकता हूं। हालांकि, यह थोड़ा मुश्किल और त्रुटि प्रवण है। मुझे लगता है कि यह एक स्वीकार्य समाधान होगा, और मुझे ऐसा एक देखना अच्छा लगेगा। उम्मीद है कि हालांकि कुछ और सुरुचिपूर्ण है।

मेरे सिर में सुरुचिपूर्ण समाधान निम्नलिखित कुछ ऐसा दिखाई देगा। हालांकि, मैं अगर भी संभव है इस तरह कुछ नहीं जानता:

with hacked_float_compare: 
    result = d1 != d2 

इस प्रकार, इस संदर्भ प्रबंधक के अंदर मैं चल बिन्दु तुलना (बस या तो अपने ही तुलना या numpy.allclose() के साथ मानक float() मूल्यों के लिए जगह दी जाएगी।

फिर, मुझे यकीन है कि यह संभव है बंदर float() पैचिंग वास्तव में नहीं किया जा सकता क्योंकि के बाद से यह C में लिखा गया है नहीं कर रहा हूँ। मैं भी अपने खुद के लिए dicts में हर चल बिन्दु मान बदलने के लिए होने से बचने के लिए करना चाहते हैं नाव वर्ग एक __eq__() है। हो सकता है कि इस सबसे अच्छा w है अरे यद्यपि?

+0

एक विकल्प फ्लोट के लिए एक रैपर बनाने और वहां '__eq__' ओवरराइड करना होगा। – NullUserException

+0

लेकिन आपको अपने सभी फ्लोट्स को 'फ़ज़ीफ्लोएट (0.5)', आदि – alexis

+0

के साथ बनाना होगा। मुझे यह दृष्टिकोण काम पता है, अगर मैं इससे बच सकता तो बस एक विशेष वस्तु/वर्ग का उपयोग नहीं करना चाहता था। मुझे वास्तव में इस उदाहरण में केवल 'अस्पष्ट' होने की तुलना की आवश्यकता है। यही कारण है कि मैं एक संदर्भ प्रबंधक का उपयोग करने की उम्मीद कर रहा था और सीमित समय के लिए बस एक अलग 'मोड' में जाना चाहता था। –

उत्तर

5

अंतर्निर्मित प्रकार के उप-वर्ग से बचें। जब आप किसी अज्ञात कारण के लिए अपनी ऑब्जेक्ट्स बदल गए हैं तो आपको पछतावा होगा। इसके बजाय प्रतिनिधिमंडल का प्रयोग करें।

import operator as op 


class FuzzyDict(object): 
    def __init__(self, iterable=(), float_eq=op.eq): 
     self._float_eq = float_eq 
     self._dict = dict(iterable) 

    def __getitem__(self, key): 
     return self._dict[key] 

    def __setitem__(self, key, val): 
     self._dict[key] = val 

    def __iter__(self): 
     return iter(self._dict) 

    def __len__(self): 
     return len(self._dict) 

    def __contains__(self, key): 
     return key in self._dict 

    def __eq__(self, other): 
     def compare(a, b): 
      if isinstance(a, float) and isinstance(b, float): 
       return self._float_eq(a, b) 
      else: 
       return a == b 
     try: 
      if len(self) != len(other): 
       return False 
      for key in self: 
       if not compare(self[key], other[key]): 
        return False 
      return True 
     except Exception: 
      return False 

    def __getattr__(self, attr): 
     # free features borrowed from dict 
     attr_val = getattr(self._dict, attr) 
     if callable(attr_val): 
      def wrapper(*args, **kwargs): 
       result = attr_val(*args, **kwargs) 
       if isinstance(result, dict): 
        return FuzzyDict(result, self._float_eq) 
       return result 
      return wrapper 
     return attr_val 

और एक उदाहरण के उपयोग: उदाहरण के लिए:

>>> def float_eq(a, b): 
...  return abs(a - b) < 0.01 
... 
>>> A = FuzzyDict(float_eq=float_eq) 
>>> B = FuzzyDict(float_eq=float_eq) 
>>> A['a'] = 2.345 
>>> A['b'] = 'a string' 
>>> B['a'] = 2.345 
>>> B['b'] = 'a string' 
>>> B['a'] = 2.3445 
>>> A == B 
True 
>>> B['a'] = 234.55 
>>> A == B 
False 
>>> B['a'] = 2.345 
>>> B['b'] = 'a strin' 
>>> A == B 
False 

और वे भी जब नेस्ट काम:

>>> A['nested'] = FuzzyDict(float_eq=float_eq) 
>>> A['nested']['a'] = 17.32 
>>> B['nested'] = FuzzyDict(float_eq=float_eq) 
>>> B['nested']['a'] = 17.321 
>>> B['b'] = 'a string' # changed before 
>>> A == B 
True 
>>> B['nested']['a'] = 17.34 
>>> A == B 
False 

dict के लिए एक पूर्ण प्रतिस्थापन में थोड़ा और अधिक कोड और शायद कुछ की आवश्यकता होगी यह देखने के लिए परीक्षण कि यह कितना मजबूत है, लेकिन उपरोक्त समाधान भी dict सुविधाओं में से कई प्रदान करता है (उदाहरण के लिए copy, setdefault, get, update आदि)


के बारे में तुम क्यों एक अंतर्निहित उपवर्ग नहीं करना चाहिए।

यह समाधान आसान और सही लगता है, लेकिन यह आमतौर पर नहीं है। सबसे पहले, भले ही आप अंतर्निहित प्रकारों को उप-वर्गीकृत कर सकें, इसका मतलब यह नहीं है कि उन्हें उप-वर्गों के रूप में उपयोग करने के लिए लिखा गया था, इसलिए आप यह जान सकते हैं कि कुछ काम करने के लिए आपको सोचा था कि आपको अधिक कोड लिखना होगा।

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

उदाहरण के लिए, list उपवर्गीकरण आप सोच सकते हैं कि, के बाद से list औजार केवल __iadd__ और __add__ आप इन दोनों तरीकों reimplementing सुरक्षित हो जाएगा, लेकिन आप गलत कर रहे हैं! तुम भी __radd__ को लागू करना चाहिए, अन्यथा भाव की तरह:

[1,2,3] + MyList([1,2,3]) 

एक सामान्य list और नहीं MyList लौट आते हैं।

संक्षेप में, बिल्ट-इन के उप-वर्ग में शुरुआत में आप क्या सोच सकते हैं उससे कहीं अधिक परिणाम हैं और यह उन प्रकारों या व्यवहारों के परिवर्तन के कारण कुछ अप्रत्याशित बग पेश कर सकता है जिनकी आप अपेक्षा नहीं करते थे। डिबगिंग भी कठिन हो जाती है क्योंकि आप लॉग में ऑब्जेक्ट्स के उदाहरणों को प्रिंट नहीं कर सकते हैं, प्रतिनिधित्व सही होगा! इस सूक्ष्म बग को पकड़ने के लिए आपको वास्तव में सभी वस्तुओं की कक्षा की जांच करनी होगी।

अपने विशिष्ट स्थिति में, यदि आप केवल एक ही विधि के अंदर शब्दकोशों कन्वर्ट करने के लिए योजना बना रहे हैं, तो आप उपवर्गीकरण dict के सबसे नुकसान से बचने के सकता है, लेकिन उस बिंदु पर क्यों आप बस एक समारोह नहीं लिखते और dict रों तुलना इसके साथ? यह अच्छी तरह से काम करना चाहिए, सिवाय इसके कि यदि आप dict एस को लाइब्रेरी फ़ंक्शन में पास करना चाहते हैं जो तुलना करता है।

+1

यह बहुत अच्छा लग रहा है। हालांकि, मेरे मामले में मुझे लगता है कि सिर्फ 'dict' subclassing ठीक हो सकता है। मैं बस तुलना करने के लिए स्थानीय रूप से dicts रूपांतरित करना चाहता हूँ। इसलिए, इस नई कक्षा का उपयोग कभी भी एक ही विधि में आंतरिक रूप से किया जाएगा। क्या यह समझ में आता है और उचित लगता है? –

+0

हालांकि, यह समाधान गलत नहीं होगा यदि 'अन्य' dict की कुंजी है जो पहले नियम में नहीं है। तो यह समाधान केवल फ़्लोटिंग पॉइंट तुलना से अधिक की तुलना में डिक्टर्स की तुलना करने के अर्थशास्त्र को बदलता है, है ना? –

+0

@ durden2.0 मैंने इसे पहले पोस्ट किया था जब मैंने इसे पहले पोस्ट किया था, और मैंने सोचा था कि यह ठीक था। इससे पहले कि मैंने पहली बार 'क्रमबद्ध (स्वयं) == क्रमबद्ध (अन्य)' की जांच की, जो इस अंतर को पढ़ता है, लेकिन मुझे लगता है कि उपरोक्त भी ठीक है। क्योंकि यदि चाबियों की संख्या अलग है, तो वह लंबाई की तुलना करके पकड़ा जाता है, और उसके बाद मैं प्रत्येक कुंजी को 'स्वयं' में जांचता हूं, और यदि यह 'अन्य' में नहीं है तो 'कीररर' उठाया जाता है जिसे पकड़ा जाता है 'अपवाद को छोड़कर' जो सही ढंग से 'गलत' लौटाता है, इसलिए यह ठीक होना चाहिए। वैसे भी, अगर परिवर्तन केवल एक विधि में होने की गारंटी है तो शायद 'dict' को उपclass ठीक है। – Bakuriu

1

एक तुलना ऑपरेटर ओवरराइड करने के लिए, आप एक व्युत्पन्न वर्ग एक अलग ऑपरेटर का उपयोग करता है परिभाषित करने के लिए की जरूरत है। तो आप इसे जिस तरह से सुझाव देते हैं वह नहीं कर सकते हैं।

class fuzzydict(dict): 
    def __eq__(self, other): 
     """Manually compare each element of `self` with `other`. 
      Float values are compared up to reasonable precision.""" 

आप के माध्यम से मंथन करना होगा: क्या तुम कर सकते हो dict से एक "फजी फ्लोट" वर्ग निकाले जाते हैं (जैसा कि @null) ने सुझाव दिया, या निकाले जाते हैं और वर्ग और है कि यह तैरता पर फजी तुलना का उपयोग करता है निर्दिष्ट है शब्दकोश तुलनात्मक तर्क का तर्क स्वयं, और संभवतः अंतर्निहित तुलना के रूप में तेज़ नहीं होगा, लेकिन आप अपने कोड में dict1 == dict2 लिखने में सक्षम होंगे। सभी (नेस्टेड) ​​शब्दकोशों के लिए के बजाय fuzzydict का उपयोग करना सुनिश्चित करें जिसमें फ़्लोट हो सकते हैं।

मैं तथापि, जोड़ना चाहिए, कि आप अनिश्चितता जोखिम रहे हैं: आपका शब्दकोशों बराबर की तुलना लेकिन थोड़ा अलग संख्या में शामिल होंगे, तो subsquent गणना आप परिणाम है कि नहीं बराबर की तुलना, जो शब्दकोश आप उपयोग के आधार पर दे सकता है। मेरी राय में जब आप उन्हें शब्दकोश में डालते हैं तो एक सुरक्षित (और सैनर) दृष्टिकोण आपके फ़्लोट्स को गोल करना होगा, ताकि वे कड़ाई से बराबर तुलना करें।

+0

हां, यह भी काम करेगा। हालांकि, मुझे अपनी खुद की ताना कक्षा बनाने और तुलनात्मक कोड को '__eq__' में तुलना करने के लिए बहुत लाभ नहीं दिख रहा है। यह सटीक कोड केवल एक विधि हो सकता है जो दो डिक्ट्स लेता है। तब मुझे हर जगह इस नए प्रकार के निर्देश का उपयोग करने या मौजूदा डिक्ट्स आदि को बदलने की ज़रूरत नहीं है। फिर, यह समाधान ठीक होगा अगर मैं इसे कई स्थानों पर कर रहा था। हालांकि, केवल एक क्षेत्र है जहां यह तुलना महत्वपूर्ण है। –

+0

इसके अलावा, इन फ़्लोटिंग पॉइंट नंबरों के साथ गणना करना वैसे भी मुश्किल है क्योंकि फ्लोटिंग पॉइंट प्रस्तुति इत्यादि। इसके अतिरिक्त, मेरे परिदृश्य में मुझे वास्तव में कोई फर्क नहीं पड़ता कि ये संख्याएं एक दूसरे की सीमा के भीतर हैं। इससे सड़क पर कोई अजीब ऑपरेशन नहीं होगा। यह कोड का एक बहुत सीमित क्षेत्र है जिसे मैं इसे लागू करना चाहता हूं। –

+0

पायथन की डॉक्टर तुलना रिकर्सिव है। यदि आप कक्षा प्राप्त करते हैं, तो अजगर रिकर्सन को संभालेगा और आपको केवल फ्लैट तर्क लागू करना होगा: गायब या अतिरिक्त कुंजी के लिए जांच करना, और मानों की तुलना करना। – alexis

2

बस संदर्भ के लिए, मुझे लगता है कि मेरी स्थिति में सबक्लासिंग सबसे अच्छा तरीका नहीं था। मैंने एक समाधान तैयार किया है कि मैं सबसे अधिक संभावना here का उपयोग करूंगा।

यह स्वीकार्य उत्तर नहीं है क्योंकि यह इस धागे से जो मैंने सीखा है उसके आधार पर एक सहयोगी दृष्टिकोण था। बस एक 'समाधान' चाहता था कि दूसरों से फायदा हो सकता है।

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