2009-01-20 17 views
25

में एक 'स्थानीय स्थिर' चर का अनुकरण करने पर विचार करें निम्नलिखित कोड:अजगर

def CalcSomething(a): 
    if CalcSomething._cache.has_key(a): 
     return CalcSomething._cache[a] 
    CalcSomething._cache[a] = ReallyCalc(a) 
    return CalcSomething._cache[a] 

CalcSomething._cache = { } 

यह सबसे आसान तरीका है मैं अजगर में एक 'स्थानीय स्थिर' चर का अनुकरण के लिए के बारे में सोच सकते हैं।
मुझे क्या परेशान करती है कि CalcSomething._cache समारोह की परिभाषा के बाहर बताया गया है है, लेकिन वैकल्पिक ऐसा ही कुछ होगा: समारोह की परिभाषा, जो वास्तव में बोझिल है अंदर

if not hasattr(CalcSomething, "_cache"): 
    setattr(CalcSomething, "_cache", { }) 

क्या कोई और शानदार तरीका है?

[संपादित करें]
बस स्पष्ट करने के लिए, यह प्रश्न स्थानीय फ़ंक्शन कैश के बारे में नहीं है, जैसा उपरोक्त उदाहरण सुझाव दे सकता है। यहां एक और छोटा उदाहरण दिया गया है जहां 'स्थैतिक स्थानीय' आसान हो सकता है:

def ParseString(s): 
    return ParseString._parser.parse(s) 
# Create a Parser object once, which will be used for all parsings. 
# Assuming a Parser object is heave on resources, for the sake of this example. 
ParseString._parser = Parser() 
+0

बीटीडब्ल्यू, आप मील ght अपने प्रश्न को अलग-अलग वाक्यांश बनाना चाहते हैं। आप स्थानीय स्थैतिक चर की तलाश नहीं कर रहे हैं, लेकिन आपके फ़ंक्शन में ज्ञापन को पेश करने के लिए। –

+0

आपका दूसरा उदाहरण पहले की तुलना में सबसे खराब है। आप datetime.datetime.strptime (डीटीएस, "% एम-% डी-% वाई% एच:% एम:% एस") वापस कर सकते हैं क्योंकि स्ट्रेटटाइम एक क्लास विधि है जो एक नया डेटाटाइम ऑब्जेक्ट बनाता है। डेटाटाइम ऑब्जेक्ट को बनाने के लिए वास्तव में कोई ज़रूरत नहीं है। – nosklo

+0

आप दूसरे उदाहरण के बारे में सही हैं। बदल गया –

उत्तर

48

एक प्रतिदेय वस्तु में इसे चालू करें (के बाद से है कि क्या यह वास्तव में है।)

class CalcSomething(object): 
    def __init__(self): 
     self._cache = {} 
    def __call__(self, a): 
     if a not in self._cache: 
      self._cache[a] = self.reallyCalc(a) 
     return self._cache[a] 
    def reallyCalc(self, a): 
     return # a real answer 
calcSomething = CalcSomething() 

अब आप calcSomething उपयोग कर सकते हैं जैसे कि यह एक समारोह थे। लेकिन यह साफ और आत्मनिर्भर रहता है।

+0

+1 - एक अन्य समाधान जो कैशिंग कार्यान्वयन को छुपाता है। यह दृष्टिकोण मेरे दिमाग को पार नहीं कर पाया, लेकिन यह मेरे साधारण सजावटी से अधिक शक्तिशाली दिखता है। – Abgan

+0

+1: कक्षा * का उपयोग * समाधान है। – nosklo

+0

उन लोगों के लिए जो कस्टम कॉलबेल का उपयोग विधियों के रूप में करना चाहते हैं, आपको __get__ को सही तरीके से लागू करना होगा (इस पर अधिक के लिए "पायथन डिस्क्रिप्टर प्रोटोकॉल" के लिए Google) –

16

इसे एक सजावट में बदल दें।

def static_var(var_name, initial_value): 
    def _set_var(obj): 
     setattr(obj, var_name, initial_value) 
     return obj 
    return _set_var 

@static_var("_cache", {}) 
def CalcSomething(a): 
    ... 
+1

क्या आप वाकई 'एक और सुरुचिपूर्ण तरीका' मानते हैं? नहीं, गंभीरता से ;-) –

+0

मैं स्थिर चर को पायथन में उपयोग करने के लिए एक सुरुचिपूर्ण पैटर्न पर विचार नहीं करता, लेकिन सजावटी कम से कम विशेषता सेटिंग के तकनीकी विवरण को समाहित करता है। पाइथन विशेषताओं के कूल उपयोग के लिए –

+0

+1। @ पॉ ऑयस्टर: कोई विचार नहीं कि आप इस सुरुचिपूर्ण पर विचार क्यों नहीं करते हैं। यदि आप इसे करने के लिए एक गैर-हैकिश तरीका चाहते हैं, तो बस एक आवृत्ति चर के साथ कैलकुलेटर क्लास का उपयोग करें। आपने स्थानीय स्थैतिक मांग की जो स्वाभाविक रूप से बदसूरत है। –

11

डेकोरेटर कि कैश बनाए रखेंगे और अपने समारोह कैशिंग कोड से दूषित नहीं किया जाएगा लिखने पर विचार:

def cacheResults(aFunc): 
    '''This decorator funcion binds a map between the tuple of arguments 
     and results computed by aFunc for those arguments''' 
    def cachedFunc(*args): 
     if not hasattr(aFunc, '_cache'): 
      aFunc._cache = {} 
     if args in aFunc._cache: 
      return aFunc._cache[args] 
     newVal = aFunc(*args) 
     aFunc._cache[args] = newVal 
     return newVal 
    return cachedFunc 

@cacheResults 
def ReallyCalc(a): 
    '''This function does only actual computation''' 
    return pow(a, 42) 

शायद यह पहली बार में बहुत अच्छी लग रही नहीं है, लेकिन आप cacheResults() कहीं भी आप उपयोग कर सकते हैं कीवर्ड पैरामीटर की आवश्यकता नहीं है। समान सजावट बनाना संभव है जो कीवर्ड पैराम्स के लिए भी काम करेगा, लेकिन इस बार यह आवश्यक नहीं था।

+0

हालांकि यह मेरे प्रश्न का उद्देश्य नहीं है (वहां स्पष्टीकरण देखें), यह स्थानीय कैशों को लागू करने के लिए एक सुंदर योजना है। उसके लिए धन्यवाद। –

+0

आप '_cache'' cacheResults' के स्थानीय चर को बनाकर 'if not hasattr'' स्थिति से छुटकारा पा सकते हैं। – GingerPlusPlus

3

एक विकल्प डिफ़ॉल्ट मानकों का दुरुपयोग करना है। अर्थात्:

def CalcSomething(a, _cache={}): 
    if _cache.has_key(a): 

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

इसे रोकने के लिए, एक बेहतर समाधान फ़ंक्शन को अपनी स्थिरता वाले बंद करने के लिए लपेटना होगा :

@apply 
def CalcSomething(): 
    cache = {} # statics go here 

    def CalcSomething(a): 
     if cache.has_key(a): 
      return cache[a] 
     cache[a] = ReallyCalc(a) 
     return cache[a] 
    return CalcSomething 
+0

दुर्भाग्यवश, लागू पायथन 3.0 से हटा दिया गया है। मुझे भी कुछ ऐसे मामलों का मुट्ठी भर मिला जहां यह छोटा, सरल और उपयोगी था। –

+0

मुझे नहीं लगता कि यह लागू करने का एक अच्छा उपयोग है। फ़ंक्शन को कॉल करने के लिए यह और अधिक स्पष्ट है। –

4

एसएलॉट द्वारा प्रस्तावित समाधान समाधान है जो मैं प्रस्तावित करता हूं।

कि सभी, मैं एक पर अपने प्रारंभिक प्रयास के लिए एक वैकल्पिक प्रदान कर रहा हूँ यह देखते हुए:

वहाँ उपयोगी "memoize" सज्जाकार के आसपास भी की तरह हैं, फ़ंक्शन और एक "स्थैतिक स्थानीय", जो स्टैंडअलोन है:

def calc_something(a): 

    try: 
     return calc_something._cache[a] 
    except AttributeError: # _cache is not there 
     calc_something._cache= {} 
    except KeyError: # the result is not there 
     pass 

    # compute result here 

    calc_something._cache[a]= result 
    return result