2009-06-16 78 views
2

本質上,我想在堆棧上放置一個變量,該變量可通過堆棧下面的所有調用訪問,直到塊退出。在Java中,我會使用支持方法的本地靜態線程解決此問題,然後可以從方法訪問。如何將變量放在Python中的堆棧/上下文中

典型示例:獲取請求並打開數據庫連接。在請求完成之前,您希望所有代碼都使用此數據庫連接。完成並關閉請求後,關閉數據庫連接。

我需要的是一個報告生成器。每個報告由多個部分組成,每個部分可以依賴於不同的計算,有時不同的部分部分依賴於相同的計算。由於我不想重複繁重的計算,我需要緩存它們。我的想法是用緩存修飾器來裝飾方法。緩存根據方法名稱和模塊創建一個id,它是參數,查看它是否已經在堆棧變量中計算了這個值,如果沒有,則執行該方法。

我會試着通過顯示我當前的實施來清除。我想要做的是簡化那些實現計算的代碼。

首先,我有中央高速緩存訪​​問對象,我稱之爲的MathContext:

class MathContext(object): 
    def __init__(self, fn): 
     self.fn = fn 
     self.cache = dict() 
    def get(self, calc_config): 
     id = create_id(calc_config) 
     if id not in self.cache: 
      self.cache[id] = calc_config.exec(self) 
     return self.cache[id] 

使用fn參數是上下文有關創建的,文件名從那裏可以讀取的數據來計算的。

然後我們計算類:

class CalcBase(object): 
    def exec(self, math_context): 
     raise NotImplementedError 

這裏是一個愚蠢的斐波那契數的例子。方法非實際上是遞歸的,他們在大集,而不是數據的工作,但它的作品來證明你將如何依賴於其他計算:

class Fibonacci(CalcBase): 
    def __init__(self, n): self.n = n 
    def exec(self, math_context): 
     if self.n < 2: return 1 
     a = math_context.get(Fibonacci(self.n-1)) 
     b = math_context.get(Fibonacci(self.n-2)) 
     return a+b 

我想斐波那契數是不是,只是一種裝飾方法:

@cache 
def fib(n): 
    if n<2: return 1 
    return fib(n-1)+fib(n-2) 

通過math_context示例,當math_context超出作用域時,所有緩存值也是如此。我想爲裝飾者做同樣的事情。 IE瀏覽器。在X點,@cache緩存的所有內容都被取消爲gced。

+0

這個設計有什麼問題?看上去不錯。看起來它滿足你的需求。也許你應該更新你的問題,以顯示你想要改變這個代碼的位置或方式。或者這個代碼做得不好。 – 2009-06-16 15:43:09

+1

最後一點是我想要的,但不知道如何實現。目前的設計是好的,但我覺得這是要輸出詳細。我寧願使用裝飾方法,因此使用和書寫變得更簡單。 IE瀏覽器。它正在清理當前的設計。 – Staale 2009-06-16 16:13:29

回答

5

我繼續做了一些可能只是做你想做的事情。它可以用作裝飾器和上下文管理器:

from __future__ import with_statement 
try: 
    import cPickle as pickle 
except ImportError: 
    import pickle 


class cached(object): 
    """Decorator/context manager for caching function call results. 
    All results are cached in one dictionary that is shared by all cached 
    functions. 

    To use this as a decorator: 
     @cached 
     def function(...): 
      ... 

    The results returned by a decorated function are not cleared from the 
    cache until decorated_function.clear_my_cache() or cached.clear_cache() 
    is called 

    To use this as a context manager: 

     with cached(function) as function: 
      ... 
      function(...) 
      ... 

    The function's return values will be cleared from the cache when the 
    with block ends 

    To clear all cached results, call the cached.clear_cache() class method 
    """ 

    _CACHE = {} 

    def __init__(self, fn): 
     self._fn = fn 

    def __call__(self, *args, **kwds): 
     key = self._cache_key(*args, **kwds) 
     function_cache = self._CACHE.setdefault(self._fn, {}) 
     try: 
      return function_cache[key] 
     except KeyError: 
      function_cache[key] = result = self._fn(*args, **kwds) 
      return result 

    def clear_my_cache(self): 
     """Clear the cache for a decorated function 
     """ 
     try: 
      del self._CACHE[self._fn] 
     except KeyError: 
      pass # no cached results 

    def __enter__(self): 
     return self 

    def __exit__(self, type, value, traceback): 
     self.clear_my_cache() 

    def _cache_key(self, *args, **kwds): 
     """Create a cache key for the given positional and keyword 
     arguments. pickle.dumps() is used because there could be 
     unhashable objects in the arguments, but passing them to 
     pickle.dumps() will result in a string, which is always hashable. 

     I used this to make the cached class as generic as possible. Depending 
     on your requirements, other key generating techniques may be more 
     efficient 
     """ 
     return pickle.dumps((args, sorted(kwds.items())), pickle.HIGHEST_PROTOCOL) 

    @classmethod 
    def clear_cache(cls): 
     """Clear everything from all functions from the cache 
     """ 
     cls._CACHE = {} 


if __name__ == '__main__': 
    # used as decorator 
    @cached 
    def fibonacci(n): 
     print "calculating fibonacci(%d)" % n 
     if n == 0: 
      return 0 
     if n == 1: 
      return 1 
     return fibonacci(n - 1) + fibonacci(n - 2) 

    for n in xrange(10): 
     print 'fibonacci(%d) = %d' % (n, fibonacci(n)) 


    def lucas(n): 
     print "calculating lucas(%d)" % n 
     if n == 0: 
      return 2 
     if n == 1: 
      return 1 
     return lucas(n - 1) + lucas(n - 2) 

    # used as context manager 
    with cached(lucas) as lucas: 
     for i in xrange(10): 
      print 'lucas(%d) = %d' % (i, lucas(i)) 

    for n in xrange(9, -1, -1): 
     print 'fibonacci(%d) = %d' % (n, fibonacci(n)) 

    cached.clear_cache() 

    for n in xrange(9, -1, -1): 
     print 'fibonacci(%d) = %d' % (n, fibonacci(n)) 
0

你可以使用全局變量包裝在一個getter函數:

def getConnection(): 
    global connection 
    if connection: 
     return connection 
    connection=createConnection() 
    return connection 
2

這個問題似乎是兩個問題

  • 一)共享數據庫連接
  • B)高速緩存/ Memoizing

b)你已經回答了你自己

a)我似乎不明白爲什麼你需要把它放在堆棧上? 你可以做的這些

  1. 一個你可以使用類和連接 可能是它的屬性
  2. 可以裝飾您的所有功能 ,使他們得到 中心位置各
  3. 連接函數可明確使用全局連接方法
  4. 您可以創建連接並在其周圍創建一個 或創建一個上下文 對象並傳遞給 情況下,連接可以是 方面

等的一部分,等

+0

我使用了一個數據庫連接,因爲我有在Java中使用這個請求的經驗,這與我嘗試的概念是一樣的。我不想使用上下文變量,因爲它強制了我正在裝飾的方法的結構,我希望裝飾器是獨立的和自我管理的。 – Staale 2009-06-16 14:40:04

0

「你的請求,並打開一個數據庫連接....你關閉數據庫連接。」

這是對象的用途。創建連接對象,將其傳遞給其他對象,然後在完成後關閉它。全局不合適。只需將該值作爲參數傳遞給正在進行工作的其他對象。

「每份報告由多個部分組成,每一部分都可以依靠不同的計算,有時不同的部分依靠在同樣的計算部分....我需要對它們進行緩存」

這是什麼對象是給。創建一個有用的計算結果的字典,並將其從報表部分傳遞給報表部分。

你不需要混淆「堆棧變量」,「靜態線程本地」或類似的東西。 只需將普通變量參數傳遞給普通的方法函數即可。你會更快樂。


class MemoizedCalculation(object): 
    pass 

class Fibonacci(MemoizedCalculation): 
    def __init__(self): 
     self.cache= { 0: 1, 1: 1 } 
    def __call__(self, arg): 
     if arg not in self.cache: 
      self.cache[arg]= self(arg-1) + self(arg-2) 
     return self.cache[arg] 

class MathContext(object): 
    def __init__(self): 
     self.fibonacci = Fibonacci() 

您可以使用它像這樣

>>> mc= MathContext() 
>>> mc.fibonacci(4) 
5 

您可以定義任意數量的計算,並將其全部摺疊成一個單一的容器對象。

如果需要,可以將MathContext製作成正式的上下文管理器,以便它可以與語句一起使用。將這兩種方法添加到MathContext中。

def __enter__(self): 
    print "Initialize" 
    return self 
def __exit__(self, type_, value, traceback): 
    print "Release" 

然後你可以做到這一點。

with MathContext() as mc: 
    print mc.fibonacci(4) 

語句的結束,就可以保證了__exit__方法被調用。

+0

計算可以依賴於其他計算,並且我希望避免在緩存中需要執行x操作:cache [x] = calc()在每個依賴於x的方法中。 Memoize模式朝着正確的方向移動,但是當我在多個文件中使用它時,沒有中央部分可以清除不再需要的數據。 – Staale 2009-06-16 14:55:25