2010-04-26 17 views
23

मेरे पास एक पायथन फ़ंक्शन है जिसमें निर्धारिती परिणाम होता है। यह एक लंबे समय से चलाने के लिए ले जाता है और एक बड़ी उत्पादन उत्पन्न करता है:फ़ंक्शन संशोधित होने पर आउटपुट को पुन: उत्पन्न करने के लिए एक पायथन फ़ंक्शन को दबाकर

def time_consuming_function(): 
    # lots_of_computing_time to come up with the_result 
    return the_result 

मैं समय-समय पर time_consuming_function संशोधित है, लेकिन मैं इसे फिर से चलाने, जबकि यह अपरिवर्तित है से बचने के लिए चाहते हैं। [time_consuming_function केवल उन कार्यों पर निर्भर करता है जो यहां मानी गई प्रयोजनों के लिए अपरिवर्तनीय हैं; यानी इसमें पाइथन पुस्तकालयों से कार्य हो सकते हैं लेकिन मेरे कोड के अन्य टुकड़ों से नहीं जो मैं बदलूंगा।] समाधान जो मुझे स्वयं को बताता है वह आउटपुट को कैश करना और फ़ंक्शन के कुछ "हैश" को कैश करना है। यदि हैश बदलता है, तो फ़ंक्शन संशोधित कर दिया जाएगा, और हमें आउटपुट को पुन: उत्पन्न करना होगा।

क्या यह संभव या हास्यास्पद है?


अपडेट किया गया: उत्तरों के आधार पर, यह है कि मैं क्या करना चाहते हैं, करने के लिए "memoize" time_consuming_function है बजाय छोड़कर तरह लग रहा है (या उनके अतिरिक्त) तर्क एक अपरिवर्तनीय समारोह में पारित कर दिया है, मैं चाहता हूँ एक फ़ंक्शन के लिए खाता जो स्वयं बदल जाएगा।

+4

+1। – zdav

+0

आप विधि को कैसे संशोधित करते हैं? क्या आप प्रोग्राम रन में हैश रखना चाहते हैं या यह एक रन के भीतर है लेकिन कुछ मॉड्यूल रीलोड में है? – doublep

+0

मेरे पास एक स्क्रिप्ट फ़ाइल में विधि होगी; मैं इसे समय-समय पर हाथ से संशोधित करता हूं। एप्लिकेशन यह है कि यह फ़ंक्शन कुछ सिमुलेशन कोड में चलने के लिए "समस्या डेटा" उत्पन्न करेगा। मैं समय-समय पर समस्या को बदल दूंगा। –

उत्तर

6

यदि मैं आपकी समस्या को समझता हूं, तो मुझे लगता है कि मैं इसे इस तरह से निपटूंगा। यह एक स्पर्श बुराई है, लेकिन मुझे लगता है कि यह उन अन्य समाधानों की तुलना में अधिक विश्वसनीय और ऑन-पॉइंट है जो मैं यहां देखता हूं।

import inspect 
import functools 
import json 

def memoize_zeroadic_function_to_disk(memo_filename): 
    def decorator(f): 
     try: 
      with open(memo_filename, 'r') as fp: 
       cache = json.load(fp) 
     except IOError: 
      # file doesn't exist yet 
      cache = {} 

     source = inspect.getsource(f) 

     @functools.wraps(f) 
     def wrapper(): 
      if source not in cache: 
       cache[source] = f() 
       with open(memo_filename, 'w') as fp: 
        json.dump(cache, fp) 

      return cache[source] 
     return wrapper 
    return decorator 

@memoize_zeroadic_function_to_disk(...SOME PATH HERE...) 
def time_consuming_function(): 
    # lots_of_computing_time to come up with the_result 
    return the_result 
+0

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

+0

@ सेठ, हां, यह मुझे समझ में आता है कि पाइथन के आंतरिक हैशिंग का उपयोग करने के लिए यहां से जो आप वास्तव में चाहते हैं वह मूल्य की तुलना करना है (न कि आपके पास हैश टकराव है और इसे नहीं पता। यह असंभव है लेकिन बहुत संभव है।) यदि आप केवल वांछित थे सबसे हालिया फ़ंक्शन को कैश करने के लिए, मैं एक ताना या हैशिंग का उपयोग नहीं करता, लेकिन केवल मूल्य की तुलना करता हूं। केवल इसलिए कि आपने कहा (आउट ऑफ़ बैंड) कि आप फ़ंक्शन के कई संस्करणों को स्टोर करना चाहते थे ताकि आप पुराने कोड पर वापस आ सकें, क्या मैंने dict का उपयोग किया था। –

+0

@ सेठ, पूरे स्रोत कोड-व्हाइटस्पेस और टिप्पणियों की तुलना में कम जानकारी स्टोर करना संभव होना चाहिए और इस तरह के इस तरह के बरकरार रहना चाहिए- लेकिन यह सकारात्मक होने के लिए कुछ सावधानी बरतता है कि आपके पास एक मैच के लिए आवश्यक और पर्याप्त शर्त है। 'f.func_code.co_code' बाइटकोड है जो फ़ंक्शन वास्तव में संग्रहीत करता है, लेकिन मुझे यकीन नहीं है कि मैं आपको वादा कर सकता हूं कि यह संकलन के बीच समान होगा या नहीं। मुझे पूरी तरह से यकीन नहीं है कि यह आपको झूठी सकारात्मक नहीं दे सकता है। –

1

फ़ंक्शन को फ़ंक्शन में डालने की बजाय, मैं फ़ंक्शन को अपनी फ़ाइल में रखूंगा। उदाहरण के लिए, time_consuming.py इसे कॉल करें। यह इस तरह कुछ दिखाई देगा:

def time_consuming_method(): 
    # your existing method here 

# Is the cached data older than this file? 
if (not os.path.exists(data_file_name) 
    or os.stat(data_file_name).st_mtime < os.stat(__file__).st_mtime): 
    data = time_consuming_method() 
    save_data(data_file_name, data) 
else: 
    data = load_data(data_file_name) 

# redefine method 
def time_consuming_method(): 
    return data 

काम करने के लिए बुनियादी ढांचे का परीक्षण करते समय, मैं धीमे हिस्सों पर टिप्पणी करता हूं। एक साधारण फ़ंक्शन बनाएं जो केवल 0 लौटाता है, अपनी संतुष्टि के लिए काम कर रहे सभी सहेजें/लोड सामान प्राप्त करें, फिर धीमी बिट्स को वापस रखें।

-1

जो आप वर्णन करते हैं वह प्रभावी रूप से memoization है। सजावटी को परिभाषित करके सबसे आम कार्यों को याद किया जा सकता है।

एक (अत्यधिक सरलीकृत) उदाहरण:

def memoized(f): 
    cache={} 
    def memo(*args): 
     if args in cache: 
      return cache[args] 
     else: 
      ret=f(*args) 
      cache[args]=ret 
      return ret 
    return memo 

@memoized 
def time_consuming_method(): 
    # lots_of_computing_time to come up with the_result 
    return the_result 

संपादित करें:

माइक ग्राहम की टिप्पणी और ओपी के अद्यतन से, यह है कि मूल्यों कार्यक्रम के विभिन्न से ज्यादा रन कैश हो जाने की जरूरत है अब स्पष्ट है। यह कैश के लिए लगातार कुछ स्टोरेज का उपयोग करके किया जा सकता है (उदाहरण के लिए Pickle या एक साधारण टेक्स्ट फ़ाइल का उपयोग करने के रूप में सरल, या शायद एक पूर्ण उड़ा डेटाबेस का उपयोग करना, या बीच में कुछ भी)। उपयोग करने के लिए कौन सी विधि का विकल्प ओपी की जरूरतों पर निर्भर करता है। कई अन्य उत्तरों पहले से ही कुछ समाधान देते हैं, इसलिए मैं इसे दोहराने वाला नहीं हूं।

+0

ऐसा प्रतीत होता है कि ओपी रनटाइम के बीच फ़ंक्शन के एक रिटर्न मान को याद रखना चाहता है। यह विभिन्न तर्कों के आधार पर एक रन के दौरान एक फ़ंक्शन के विभिन्न रिटर्न मानों को कैश करता है। –

+0

@ माइक ग्राहम: धन्यवाद, मैंने अपना जवाब अपडेट किया। – MAK

0

तो, यहाँ एक बहुत साफ चाल का उपयोग कर सज्जाकार है:

 
def memoize(f): 
    cache={}; 
    def result(*args): 
     if args not in cache: 
      cache[args]=f(*args); 
     return cache[args]; 
    return result; 
ऊपर के साथ

, तो आप उपयोग कर सकते हैं:

 
@memoize 
def myfunc(x,y,z): 
    # Some really long running computation 

जब आप myfunc आह्वान, आप वास्तव में memoized लागू किया जाएगा इसका संस्करण बहुत साफ, हुह? जब भी आप अपने समारोह को फिर से परिभाषित करना चाहते हैं, बस का उपयोग फिर से "@memoize", या स्पष्ट रूप से लिखें:

 
myfunc = memoize(new_definition_for_myfunc); 

संपादित
मुझे नहीं पता था कि आप एक से अधिक रन के बीच कैश करने के लिए करना चाहता था।उस स्थिति में, आप निम्न कार्य कर सकते हैं:

 
import os; 
import os.path; 
import cPickle; 

class MemoizedFunction(object): 
    def __init__(self,f): 
     self.function=f; 
     self.filename=str(hash(f))+".cache"; 
     self.cache={}; 
     if os.path.exists(self.filename): 
      with open(filename,'rb') as file: 
       self.cache=cPickle.load(file); 

    def __call__(self,*args): 
     if args not in self.cache: 
      self.cache[args]=self.function(*args); 
     return self.cache[args]; 

    def __del__(self): 
     with open(self.filename,'wb') as file: 
       cPickle.dump(self.cache,file,cPickle.HIGHEST_PROTOCOL); 

def memoize(f): 
    return MemoizedFunction(f); 
+0

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

+0

ऐसा प्रतीत होता है कि ओपी रनटाइम के बीच फ़ंक्शन के एक रिटर्न मान को याद रखना चाहता है। यह विभिन्न तर्कों के आधार पर एक रन के दौरान एक फ़ंक्शन के विभिन्न रिटर्न मानों को कैश करता है। –

+0

@ माइक, ठीक है। मुझे नहीं पता था कि यह कार्यक्रम के रनों के बीच था। –

0

पहला भाग आपकी लुकअप तालिका का ज्ञापन और क्रमबद्धरण है। यह कुछ पायथन serialization पुस्तकालय के आधार पर पर्याप्त सरल होना चाहिए। दूसरा भाग यह है कि जब आप स्रोत कोड बदलते हैं तो आप अपनी धारावाहिक लुकअप तालिका को हटाना चाहते हैं। शायद यह कुछ फैंसी समाधान में overthought किया जा रहा है। संभवतः जब आप कोड बदलते हैं तो आप इसे कहीं भी चेक करते हैं? क्यों नहीं अपने चेकइन दिनचर्या में एक हुक जोड़ें जो आपकी धारावाहिक तालिका को हटा देता है? या यदि यह शोध डेटा नहीं है और उत्पादन में है, तो इसे अपनी रिलीज प्रक्रिया का हिस्सा बनाएं कि यदि आपकी फ़ाइल की संशोधन संख्या (इस फ़ंक्शन को अपनी फ़ाइल में डाल दें) बदल गई है, तो आपकी रिलीज स्क्रिप्ट सीरियलज़ेड लुकअप टेबल हटा देती है। दिलचस्प समस्या के लिए

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