문제

본질적으로, 나는 스택에 변수를 넣고 싶습니다. 블록이 종료 될 때까지 스택의 해당 부품 아래의 모든 통화로 도달 할 수 있습니다. Java에서는 지원 방법이있는 정적 스레드 로컬을 사용하여 이것을 해결 한 다음 방법에서 액세스 할 수 있습니다.

일반적인 예 : 요청을 받고 데이터베이스 연결을 엽니 다. 요청이 완료 될 때까지 모든 코드 가이 데이터베이스 연결을 사용하려고합니다. 요청을 마치고 닫은 후 데이터베이스 연결을 닫습니다.

내가 필요한 것은 보고서 생성기입니다. 각 보고서는 여러 부분으로 구성되며 각 부분은 다른 계산에 의존 할 수 있으며 때로는 다른 부분이 동일한 계산에 부분적으로 의존합니다. 무거운 계산을 반복하고 싶지 않기 때문에 캐시를 캐시해야합니다. 내 생각은 캐시 데코레이터로 방법을 장식하는 것입니다. 캐시는 메소드 이름과 모듈을 기반으로 ID를 생성하고 인수는 스택 변수 로이 모든 준비가 계산 된 경우를보고 그렇지 않은 경우 메소드를 실행합니다.

현재 구현을 보여줌으로써 시도하고 문의하겠습니다. 내가하고 싶은 것은 계산을 구현하는 사람들의 코드를 단순화하는 것입니다.

먼저 MathContext라고하는 Central Cache Access 객체가 있습니다.

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

내가 fibonacci가 대신하기를 원하는 것은 단지 장식 된 방법 일뿐입니다.

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

Math_Context 예제를 사용하면 Math_Context가 범위를 벗어나면 캐시 된 값도 마찬가지입니다. 나는 데코레이터와 같은 것을 원한다. 즉. X 지점에서 @Cache로 캐시 된 모든 것은 GCED로 구성됩니다.

도움이 되었습니까?

해결책

나는 계속해서 당신이 원하는 것을 할 수있는 무언가를 만들었습니다. 데코레이터와 컨텍스트 관리자로 사용할 수 있습니다.

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))

다른 팁

이 질문은 두 가지 질문 인 것 같습니다

  • a) DB 연결 공유
  • b) 캐싱/메모 잉

b) 당신은 스스로 대답했습니다

a) 왜 스택에 넣어야하는지 이해하지 못하는 것 같습니다. 당신은 이것들 중 하나를 할 수 있습니다

  1. 클래스를 사용할 수 있으며 연결이 속성이 될 수 있습니다.
  2. 중앙 위치에서 연결을 얻도록 모든 기능을 장식 할 수 있습니다.
  3. 각 함수는 글로벌 연결 방법을 명시 적으로 사용할 수 있습니다
  4. 연결을 생성하고 주변을 전달하거나 컨텍스트 객체를 만들고 컨텍스트를 전달할 수 있습니다. 연결이 컨텍스트의 일부가 될 수 있습니다.

기타 등

getter 기능으로 랩핑 된 글로벌 변수를 사용할 수 있습니다.

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

"요청을 받고 데이터베이스 연결을 엽니 다 .... 데이터베이스 연결을 닫습니다."

이것이 객체의 목적입니다. 연결 객체를 만들고 다른 객체로 전달한 다음 완료되면 닫으십시오. 글로벌은 적절하지 않습니다. 작업을 수행하는 다른 객체에 매개 변수로 주변의 값을 전달합니다.

"각 보고서는 여러 부분으로 구성되며 각 부품은 다른 계산에 의존 할 수 있으며 때로는 다른 부분이 동일한 계산에 부분적으로 의존합니다 .... 캐시가 필요합니다."

이것이 객체의 목적입니다. 유용한 계산 결과를 가진 사전을 만들고 보고서 부분에서 보고서를 전달하십시오.

"스택 변수", "정적 스레드 로컬"또는 그와 비슷한 것을 엉망으로 만들 필요가 없습니다. 일반 변수 인수를 일반 방법 기능에 전달하십시오. 당신은 훨씬 더 행복 할 것입니다.


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__ 방법이 호출되었습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top