2009-06-16 52 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