2014-10-29 3 views
7

मेरे पास एक साधारण छोटा सजावट है, जो कैश को फ़ंक्शन एट्रिब्यूट के रूप में dict में फ़ंक्शन कॉल से परिणाम देता है।किसी फ़ंक्शन विशेषता को पुन: असाइन करें, यह 'पहुंच योग्य' बनाता है

from decorator import decorator 
def _dynamic_programming(f, *args, **kwargs): 
    try: 
     f.cache[args] 
    except KeyError: 
     f.cache[args] = f(*args, **kwargs) 
    return f.cache[args] 

def dynamic_programming(f): 
    f.cache = {} 
    return decorator(_dynamic_programming, f) 

अब मैं कैश को खाली करने की संभावना जोड़ना चाहता हूं। तो मैं इस तरह dynamic_programming() समारोह बदलने के लिए:

def dynamic_programming(f): 
    f.cache = {} 
    def clear(): 
     f.cache = {} 
    f.clear = clear 
    return decorator(_dynamic_programming, f) 

अब मैं एक फाइबोनैचि संख्या समारोह को लागू करने के लिए इस छोटी बात का उपयोग मान लें:

@dynamic_programming 
def fib(n): 
    if n <= 1: 
     return 1 
    else: 
     return fib(n-1) + fib(n-2) 

>>> fib(4) 
5 
>>> fib.cache 
{(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5} 

लेकिन अब जब मैं कैश कुछ अजीब स्पष्ट होता है:

:
>>> fib.clear() 
>>> fib.cache 
{(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5} 

या (एक नई पायथन गिरी चल के साथ) यह इसका उल्टा करना

>>> fib.clear() 
>>> fib(4) 
5 
>>> fib.cache 
{} 

कैश किसी भी तरह से पहली पहुंच के बाद 'पहुंचने योग्य' क्यों नहीं है, यानी clear() पर कॉल करने के बाद या clear() के बाद कॉल करने पर कॉल क्यों नहीं हो रहा है?

(। Btw मैं एक समाधान सही ढंग से कैश को साफ़ करने के लिए पता है:। f.cache.clear() बुला के बजाय यह करने के लिए {} बताए काम करता है की उम्मीद के रूप में मैं केवल कारण क्यों बताए समाधान में विफल रहता है में दिलचस्पी रखता हूँ।)

उत्तर

7

समस्या decorator मॉड्यूल के साथ है। (डुप्लिकेट लाइनों को छोड़ दिया)

from decorator import decorator 
def _dynamic_programming(f, *args, **kwargs): 
    print "Inside decorator", id(f.cache) 
    try: 
     f.cache[args] 
    except KeyError: 
     f.cache[args] = f(*args, **kwargs) 
    return f.cache[args] 

def dynamic_programming(f): 
    f.cache = {} 
    print "Original cache", id(f.cache) 
    def clear(): 
     f.cache = {} 
     print "New cache", id(f.cache) 
    f.clear = clear 
    return decorator(_dynamic_programming, f) 

@dynamic_programming 
def fib(n): 
    if n <= 1: 
     return 1 
    else: 
     return fib(n-1) + fib(n-2) 

print fib(4) 
print id(fib.cache) 
fib.clear() 
print id(fib.cache) 
print fib(10) 
print id(fib.cache) 

यह आउटपुट:: यदि आप अपने डेकोरेटर करने के लिए कुछ print बयान जोड़ देते हैं तो

Original cache 139877501744024 
Inside decorator 139877501744024 
5 
139877501744024 
New cache 139877501802208 
139877501744024 
Inside decorator 139877501802208 
89 
139877501744024 

आप देख सकते हैं, cache स्पष्ट समारोह अनुसार डेकोरेटर परिवर्तन के अंदर। हालांकि, cache__main__ से उपयोग नहीं किया गया है। cache मुद्रण के बाहर और डेकोरेटर के अंदर इसकी स्पष्ट जानकारी (फिर से, डुप्लिकेट को छोड़ दिया):

Inside decorator {} 
Inside decorator {(1,): 1} 
Inside decorator {(2,): 2, (0,): 1, (1,): 1} 
Inside decorator {(2,): 2, (0,): 1, (3,): 3, (1,): 1} 
5 
Outside {(2,): 2, (0,): 1, (3,): 3, (1,): 1, (4,): 5} 
Inside decorator {} 
Inside decorator {(1,): 1} 
Inside decorator {(2,): 2, (0,): 1, (1,): 1} 
Inside decorator {(2,): 2, (0,): 1, (3,): 3, (1,): 1} 
Inside decorator {(2,): 2, (0,): 1, (3,): 3, (1,): 1, (4,): 5} 
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5, (5,): 8} 
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5, (5,): 8, (6,): 13} 
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5, (5,): 8, (6,): 13, (7,): 21} 
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (8,): 34, (3,): 3, (4,): 5, (5,): 8, (6,): 13, (7,): 21} 
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (8,): 34, (3,): 3, (9,): 55, (4,): 5, (5,): 8, (6,): 13, (7,): 21} 
89 
Outside {(2,): 2, (0,): 1, (3,): 3, (1,): 1, (4,): 5} 

आप देख सकते हैं, के अंदर परिवर्तन बाहर की दुनिया में गूँजती नहीं कर रहे हैं।

self.dict = func.__dict__.copy() 

और फिर later:

func.__dict__ = getattr(self, 'dict', {}) 

तो बुनियादी तौर पर, बाहर की दुनिया में __dict__ है समस्या the decorator module अंदर, वहाँ लाइन (वर्ग के भीतर यह डेकोरेटर बनाने के लिए प्रयोग किया जाता) है अंदर __dict__ से अलग।इसका मतलब है कि:

  • __dict__ कॉपी किया जाता है (संदर्भित नहीं) डेकोरेटर
  • cache परिवर्तन, यह परिवर्तन जब अंदर __dict__, नहीं बाहर __dict__
  • इसलिए, cache_dynamic_programming द्वारा प्रयोग किया जाता द्वारा साफ़ किया गया है, लेकिन आप बाहर से नहीं देख सकते हैं, क्योंकि सजावटी के __dict__ पुराने cache पर इंगित कर रहे हैं (जैसा कि आप ऊपर देख सकते हैं, अंदर cache अपडेट के अंदर, जबकि cache बाहरी रहता है)

तो, संक्षेप में प्रस्तुत करने, यह decorator मॉड्यूल के साथ एक समस्या है।

+2

मेरे सबसे लंबे समय तक पद पर अभी तक , इसे देखकर ... :) – matsjoyce

3

इसलिए @ matsjoyce का जवाब बहुत ही दिलचस्प और गहराई है और मुझे पता है आप पहले से ही समाधान है, लेकिन मैं हमेशा यह थोड़ा स्पष्ट अपने ही सज्जाकार लिखने के लिए लगता है:

def dynamic_programming(f): 
    def wrapper(*args, **kwargs): 
     try: 
      return wrapper.cache[args]    
     except KeyError: 
      res = wrapper.cache[args] = f(*args, **kwargs) 
      return res 
    wrapper.cache = {} 
    wrapper.clear = wrapper.cache.clear 
    return wrapper 
+2

अच्छा बिंदु। इस तरह की समस्याओं के साथ, यह मॉड्यूल के स्रोत में खोदने के बिना किसी भी विषमता को देखने में मदद करता है ... – matsjoyce

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