2012-07-05 28 views
8

मैंने किसी चीज़ उपवर्ग जो एक गतिशील प्रेषण __ iter __ लागू करता है एक कैशिंग जनरेटर का उपयोग किया है के रूप में कार्यान्वित तो की तरह (मैं भी आईटीईआर कैश अमान्य के लिए एक विधि है):__iter __() एक जनरेटर

def __iter__(self): 
    print("iter called") 
    if self.__iter_cache is None: 
     iter_seen = {} 
     iter_cache = [] 
     for name in self.__slots: 
      value = self.__slots[name] 
      iter_seen[name] = True 
      item = (name, value) 
      iter_cache.append(item) 
      yield item   
     for d in self.__dc_list: 
      for name, value in iter(d): 
       if name not in iter_seen: 
        iter_seen[name] = True 
        item = (name, value) 
        iter_cache.append(item) 
        yield item 
     self.__iter_cache = iter_cache 
    else: 
     print("iter cache hit") 
     for item in self.__iter_cache: 
      yield item 

यह लगता है काम कर रहे हैं ... क्या कोई गठिया है जिसके बारे में मुझे पता नहीं हो सकता है? क्या मैं कुछ हास्यास्पद कर रहा हूँ?

+2

मैं कम से कम 'iter_seen' संरचना के लिए एक' dict' के बजाय एक [ 'set'] (http://docs.python.org/library/stdtypes.html#set) का प्रयोग करेंगे। –

+0

एचएम, यह वास्तव में मुझे क्या हासिल करेगा? चूंकि मुझे सेट बीजगणित की आवश्यकता नहीं है, इसलिए यह अधिक उचित और हल्के कार्यान्वयन नहीं होगा? –

+2

'_ में _ _ के साथ 'i _ में' _ के साथ 'बदलें। आपको 'स्टेटर' के लिए 'iter' की आवश्यकता नहीं है – jfs

उत्तर

1

यह एक बहुत नाजुक दृष्टिकोण की तरह लगता है। ऑब्जेक्ट को असंगत स्थिति में रखने के लिए सक्रिय पुनरावृत्ति के दौरान __slots, __dc_list, __iter_cache को बदलने के लिए पर्याप्त है।

आपको या तो पुनरावृत्ति के दौरान वस्तु को बदलने या सभी कैश वस्तुओं को एक बार में उत्पन्न करने और सूची की एक प्रति वापस करने की आवश्यकता है।

+0

सच है। __slots केवल __setitem__ या __delitem__ द्वारा बदला जाता है, जब मैं जनरेटर सक्रिय होता हूं तो मैं उन ऑप्स को आसानी से रोक सकता हूं (एक्सा बढ़ाएं)। __dc_list वर्तमान में केवल __init__ में सेट/बदला गया है, अगर मैं इसे अपडेट करने के लिए कोई विधि जोड़ता हूं (मुझे संभवतः) मुझे __slots से प्रतिबंधित semantics की प्रतिलिपि बनाने की आवश्यकता है। __iter_cache कोई समस्या नहीं है। यह केवल __iter__ द्वारा अपडेट किया गया है, और केवल पूरे अनुक्रम के बाद ही गणना की गई है। –

+1

एकाधिक समवर्ती पुनरावृत्तियों कुछ पहलुओं में मल्टीथ्रेडिंग के समान हैं। यदि वस्तुओं अपरिवर्तनीय हैं तो इसके बारे में तर्क करना बहुत आसान है। तीन पुनरावृत्तियों की कल्पना करें: पहला कैश पॉप्युलेट करता है, तीसरा कैश का उपयोग करता है, दूसरा कैश सेट होने से पहले कुछ समय शुरू होता है लेकिन ऑब्जेक्ट बदल जाता है (यह नए मान देख सकता है, उसके बाद तीसरा पुनरावृत्ति शुरू किया गया था) – jfs

+1

I इस साइट से प्यार करो। आपके त्वरित और टू-द-पॉइंट उत्तरों ने नाटकीय रूप से मेरे पीई ज्ञान में वृद्धि की है, धन्यवाद दोस्तों! –

2

container.__iter__() एक पुनरावर्तक वस्तु देता है। इटरेटर वस्तुओं खुद को निम्नलिखित दो विधियों, जो एक साथ इटरेटर प्रोटोकॉल के रूप में समर्थन करने के लिए आवश्यक हैं:

iterator.__iter__() 

रिटर्न इटरेटर वस्तु ही।

iterator.next() 

कंटेनर से अगला आइटम लौटें।

यह बिल्कुल जेनरेटर के समान है। तो किसी भी दुष्प्रभाव से डरो मत।

+3

एक कंटेनर ऑब्जेक्ट की '__iter __()' विधि बनाना एक या अधिक 'उपज' कथन का उपयोग करके जनरेटर एक सामान्य शॉर्टकट है जो एक अलग इटरेटर वर्ग और इसकी विधियों को स्पष्ट रूप से परिभाषित और कोड करने से बचाता है। – martineau

2

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

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

+0

अलगाव के बारे में अच्छा बिंदु, मैंने वास्तव में पहले कोशिश की :-) –

0

आप जो भी कर रहे हैं वह अजीब है हालांकि वैध है। __slots या __dc_list क्या है ?? आम तौर पर अपने ऑब्जेक्ट की सामग्री को इसके प्रकार के बजाय विशेषता नाम में वर्णित करना बेहतर होता है (उदाहरण: self.u_list के बजाय self.users)।

आप इसे मेरे सरल बनाने के लिए अपने LazyProperty सजावट का उपयोग कर सकते हैं।

बस अपनी विधि @LazyProperty के साथ सजाने के लिए। इसे पहली बार बुलाया जाएगा, और सजावटी तब परिणामों के साथ विशेषता को प्रतिस्थापित करेगा। एकमात्र आवश्यकता यह है कि मूल्य दोहराया जा सकता है; यह परिवर्तनीय स्थिति पर निर्भर नहीं है। आपके पास अपने वर्तमान कोड में यह आवश्यकता भी है, अपने स्वयं के .__ iter_cache के साथ।

def __iter__(self) 
    return self.__iter 

@LazyProperty 
def __iter(self) 
    def my_generator(): 
     yield whatever 
    return tuple(my_generator()) 
+0

संभवतः अजीब। स्लॉट ऑब्जेक्ट्स (ओवरराइड) गुण हैं और डीसी_लिस्ट प्रोटोटाइप ऑब्जेक्ट्स की एक सूची है (रिकर्सिवली) कॉपी स्लॉट से। मैं पी में स्वयं के प्रतिनिधिमंडल मेचनिश्म जैसे कुछ को लागू करने की कोशिश कर रहा हूं। –

+0

'__iter__' को एक पुनरावर्तक वापस करना होगा, ट्यूपल एक नहीं है। – jfs

+0

अच्छा बिंदु, मैं अपने_जेनरेटर को वापस करने के लिए __iter बदल दूंगा –