2011-03-14 63 views
6

是否有一種Pythonic的方式來封裝一個懶惰的函數調用,從而在第一次使用函數f()時,它調用一個先前的函數g(Z)並在連續調用f()返回一個緩存值?Python懶惰求值器

請注意,memoization可能不是一個完美的選擇。

我:

f = g(Z) 
if x: 
    return 5 
elif y: 
    return f 
elif z: 
    return h(f) 

代碼工作,但我想它重組,使g(Z)如果使用值僅調用。我不想改變g(...)的定義,並且Z對緩存有點大。

編輯:我認爲f必須是一個函數,但可能並非如此。

+2

我不確定這通常是什麼意思* lazy *。更安全地稱它*緩存*或*記憶*。 – 2011-03-14 05:29:00

+0

@John Y是對的:「懶惰評估」是指不計算不會影響包含表達式結果的表達式的結果,例如。在'f()和g()'中,如果f()是'False',懶惰評估將不會調用'g()'。這個問題不是關於這個問題。 – detly 2011-03-14 05:39:59

+0

當有函數參數時,它是memoization。否則,它只是一個懶惰的函數調用。 – 2011-03-14 05:41:09

回答

6

無論你尋求緩存還是懶惰評估,我都有點困惑。對於後者,請查看模塊lazy.py by Alberto Bertogli

+0

我很確定這正是我正在尋找的。 – 2011-03-14 17:01:05

2

嘗試使用這個裝飾: (這裏找到:Is there a decorator to simply cache function return values?由Alex馬爾泰利)

編輯:這裏是另一個性質的形式(使用:

class Memoize: 
    def __init__ (self, f): 
     self.f = f 
     self.mem = {} 
    def __call__ (self, *args, **kwargs): 
     if (args, str(kwargs)) in self.mem: 
      return self.mem[args, str(kwargs)] 
     else: 
      tmp = self.f(*args, **kwargs) 
      self.mem[args, str(kwargs)] = tmp 
      return tmp 

http://snippets.dzone.com/posts/show/4840/https://web.archive.org/web/20081026130601/http://snippets.dzone.com/posts/show/4840從死鏈接中提取) __get__http://code.activestate.com/recipes/363602/

1

這裏有相當多的裝飾者用於記憶:

http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize http://code.activestate.com/recipes/498110-memoize-decorator-with-o1-length-limited-lru-cache/ http://code.activestate.com/recipes/496879-memoize-decorator-function-with-cache-size-limit/

提出一個完全通用的解決方案是硬比你想象的要多。例如,您需要注意不可散列的函數參數,並且您需要確保緩存不會變得太大。

如果你真的在尋找一個懶惰的函數調用(其中一個函數只在需要的時候才被實際評估),你可以使用生成器來實現。

編輯:所以我想你真正想要的是最後的懶惰評估。這裏有可能是一個庫,你在找什麼:

http://pypi.python.org/pypi/lazypy/0.5

1

您可以使用一個緩存裝飾,讓看一個例子

from functools import wraps 

class FuncCache(object): 
    def __init__(self): 
     self.cache = {} 

    def __call__(self, func): 
     @wraps(func) 
     def callee(*args, **kwargs): 
      key = (args, str(kwargs)) 
      # see is there already result in cache 
      if key in self.cache: 
       result = self.cache.get(key) 
      else: 
       result = func(*args, **kwargs) 
       self.cache[key] = result 
      return result 
     return callee 

與緩存的裝飾,在這裏你可以寫

my_cache = FuncCache() 

@my_cache 
def foo(n): 
    """Expensive calculation 

    """ 
    sum = 0 
    for i in xrange(n): 
     sum += i 
    print 'called foo with result', sum 
    return sum 

print foo(10000) 
print foo(10000) 
print foo(1234) 

正如你可以從輸出看到

called foo with result 49995000 
49995000 
49995000 

foo只會被調用一次。你不必改變你的函數foo的任何一行。這是裝飾者的力量。

0

即使在您編輯之後,還有一系列的評論都是狡猾的,我還是不太明白。在你的第一句話中,你說第一次調用f()應該調用g(),但隨後返回緩存的值。但是,在你的評論中,你說「g()不會被調用,不管是什麼」(重點是我的)。我不知道你在否定什麼:你是說g()應該調用從來沒有(沒有多大意義;爲什麼g()存在?);或者g()可能調用,但可能不會(當然,這仍然與第一次調用f()時調用g()相矛盾。然後,您給出一個不涉及g()的片段,並且實際上不涉及到您的問題的第一句話,或者與您的問題的第一句相關,或者與註釋線索有關。

如果你再去編輯它,這裏是我的響應片段:

我:

a = f(Z) 
if x: 
    return 5 
elif y: 
    return a 
elif z: 
    return h(a) 

代碼工作,但我想 重組它,這樣如果使用該值,則f(Z)僅爲 。我不想 想要更改 f(...)的定義,並且Z對於緩存有點大。

如果這真的是你的問題,那麼答案很簡單

if x: 
    return 5 
elif y: 
    return f(Z) 
elif z: 
    return h(f(Z)) 

這是如何實現「F(Z),如果該值用於僅稱爲」。

我不完全理解「Z是有點大的緩存」。如果你的意思是在程序執行過程中會有太多不同的Z值,那麼記憶就毫無用處,那麼你可能不得不求助於預先計算f(Z)的所有值並在運行時查看它們。如果你不能這樣做(因爲你不知道你的程序會遇到Z的值),那麼你就回到了記憶中。如果這還太慢,那麼你唯一真正的選擇是使用比Python更快的東西(嘗試使用Psyco,Cython,ShedSkin或手工編碼的C模塊)。

+0

或者,如果'f(Z)'表達式比實際中的長,那麼只需要兩個單獨的'if'語句,第二個嵌套在第一個'else'子句中。 – ncoghlan 2011-03-14 10:13:36

+0

我試圖按照我認爲的方式編寫代碼,所以我希望f(Z)在我進入'if'之前被綁定到某個名稱。那麼如果我需要f(Z)的結果,我可以查詢它的長度,使用它的值等,我知道它只會在需要的時候被創建。 – 2011-03-14 17:00:48

1

Here「SA很簡單懶惰的裝飾,但缺乏使用@functools.wraps(和實際返回的實例Lazy加上一些其他潛在的陷阱):

class Lazy(object): 
    def __init__(self, calculate_function): 
     self._calculate = calculate_function 

    def __get__(self, obj, _=None): 
     if obj is None: 
      return self 
     value = self._calculate(obj) 
     setattr(obj, self._calculate.func_name, value) 
     return value 


# Sample use: 

class SomeClass(object): 

    @Lazy 
    def someprop(self): 
     print 'Actually calculating value' 
     return 13 


o = SomeClass() 
o.someprop 
o.someprop