itsource

단순히 함수 반환값을 캐시할 수 있는 데코레이터가 있나요?

mycopycode 2022. 9. 19. 23:41
반응형

단순히 함수 반환값을 캐시할 수 있는 데코레이터가 있나요?

다음 사항을 고려하십시오.

@property
def name(self):

    if not hasattr(self, '_name'):

        # expensive calculation
        self._name = 1 + 1

    return self._name

처음이지만 캐싱은 장식가에도 반영될 수 있을 것 같아요.다만, 이것과 같은 것은 발견하지 못했습니다.;)

PS 실제 계산은 변동 가능한 값에 의존하지 않습니다.

Python 3.2부터는 데코레이터가 내장되어 있습니다.

@functools.lru_cache(maxsize=100, typed=False)

데코레이터: 함수를 메모화 콜 가능으로 래핑하여 최신 콜의 최대 크기를 저장합니다.고비용 함수 또는 I/O 바운드 함수를 같은 인수로 주기적으로 호출할 때 시간을 절약할 수 있습니다.

Fibonacci 번호를 계산하기 위한 LRU 캐시의 예:

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> print([fib(n) for n in range(16)])
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> print(fib.cache_info())
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

Python 2.x를 사용할 수 없는 경우 다른 호환 메모 라이브러리의 목록은 다음과 같습니다.

3.8 파 33.8functools.cached_property

https://docs.python.org/dev/library/functools.html#functools.cached_property

cached_propertyWerkzeug에서 언급되었지만, https://stackoverflow.com/a/5295190/895245에서 파생된 것으로 생각되는 버전이 3.8로 통합될 예정입니다.이것은 놀라운 일입니다.

는 캐싱이라고 볼 수 .@property@functools.lru_cache툴툴툴툴툴

의사는 다음과 같이 말합니다.

@functools.cached_property(func)

클래스의 메서드를 값이 한 번 계산된 후 인스턴스의 수명 동안 일반 속성으로 캐시되는 속성으로 변환합니다.property()와 유사하며 캐시를 추가합니다.실질적으로 불변하는 인스턴스의 값비싼 계산 속성에 유용합니다.

예:

class DataSet:
    def __init__(self, sequence_of_numbers):
        self._data = sequence_of_numbers

    @cached_property
    def stdev(self):
        return statistics.stdev(self._data)

    @cached_property
    def variance(self):
        return statistics.variance(self._data)

버전 3.8의 신기능

주의: 이 데코레이터에서는 각 인스턴스의 dict 속성이 가변 매핑이어야 합니다.즉, 메타클래스(타입 인스턴스의 dict 속성은 클래스 네임스페이스의 읽기 전용 프록시이기 때문에), 정의된 슬롯 중 하나로 dict를 포함하지 않고 슬롯을 지정하는 타입(이러한 클래스는 dict 속성을 전혀 제공하지 않습니다).

범용 메모화 데코레이터를 필요로 하지 않는 것처럼 들립니다(즉, 다른 인수 값에 대한 반환 값을 캐시하려는 일반적인 경우에는 관심이 없습니다.즉, 다음과 같은 것을 원합니다.

x = obj.name  # expensive
y = obj.name  # cheap

범용 메모 데코레이터는 다음과 같은 정보를 제공합니다.

x = obj.name()  # expensive
y = obj.name()  # cheap

method-call 구문은 고가의 계산 가능성을 나타내지만 속성 구문은 빠른 검색을 나타내므로 method-call 구문이 더 나은 스타일임을 제안합니다.

[갱신:이전에 여기서 링크하여 인용한 클래스 베이스 메모 데코레이터는 메서드에 적합하지 않습니다.데코레이터 기능으로 대체했습니다.]범용 메모 데코레이터를 사용할 의향이 있다면 다음과 같은 간단한 방법이 있습니다.

def memoize(function):
  memo = {}
  def wrapper(*args):
    if args in memo:
      return memo[args]
    else:
      rv = function(*args)
      memo[args] = rv
      return rv
  return wrapper

사용 예:

@memoize
def fibonacci(n):
  if n < 2: return n
  return fibonacci(n - 1) + fibonacci(n - 2)

캐시 크기에 제한이 있는 다른 메모 장식기를 여기서 찾을 수 있습니다.

class memorize(dict):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args):
        return self[args]

    def __missing__(self, key):
        result = self[key] = self.func(*key)
        return result

사용 예:

>>> @memorize
... def foo(a, b):
...     return a * b
>>> foo(2, 4)
8
>>> foo
{(2, 4): 8}
>>> foo('hi', 3)
'hihihi'
>>> foo
{(2, 4): 8, ('hi', 3): 'hihihi'}

functools.cache는 Python 3.9(docs):

from functools import cache

@cache
def factorial(n):
    return n * factorial(n-1) if n else 1

이전 Python 버전에서는 초기 답변하나가 여전히 유효한 솔루션입니다.사용.lru_cache일반 캐시로 사용할 수 있습니다.(표준)

max size가 None으로 설정되어 있는 경우 LRU 기능은 디세이블이 되어 캐시는 바인드 없이 확장할 수 있습니다.

다음은 더 예쁜 버전입니다.

cache = lru_cache(maxsize=None)

@cache
def func(param1):
   pass

는 Werkzeuk wer를 있다.cached_property데코레이터(메모리, 소스)

기능 응답을 캐시하기 위해 이 간단한 데코레이터 클래스를 코드화했습니다.제 프로젝트에 매우 유용하다고 생각합니다.

from datetime import datetime, timedelta 

class cached(object):
    def __init__(self, *args, **kwargs):
        self.cached_function_responses = {}
        self.default_max_age = kwargs.get("default_cache_max_age", timedelta(seconds=0))

    def __call__(self, func):
        def inner(*args, **kwargs):
            max_age = kwargs.get('max_age', self.default_max_age)
            if not max_age or func not in self.cached_function_responses or (datetime.now() - self.cached_function_responses[func]['fetch_time'] > max_age):
                if 'max_age' in kwargs: del kwargs['max_age']
                res = func(*args, **kwargs)
                self.cached_function_responses[func] = {'data': res, 'fetch_time': datetime.now()}
            return self.cached_function_responses[func]['data']
        return inner

용도는 간단합니다.

import time

@cached
def myfunc(a):
    print "in func"
    return (a, datetime.now())

@cached(default_max_age = timedelta(seconds=6))
def cacheable_test(a):
    print "in cacheable test: "
    return (a, datetime.now())


print cacheable_test(1,max_age=timedelta(seconds=5))
print cacheable_test(2,max_age=timedelta(seconds=5))
time.sleep(7)
print cacheable_test(3,max_age=timedelta(seconds=5))

면책사항: 저는 kids.cache의 저자입니다.

해 주세요.kids.cache , , 을합니다.@cache.종속성이 없습니다. 최대 100줄의 코드입니다.예를 들어 코드를 염두에 두고 다음과 같이 사용할 수 있습니다.

pip install kids.cache

그리고나서

from kids.cache import cache
...
class MyClass(object):
    ...
    @cache            # <-- That's all you need to do
    @property
    def name(self):
        return 1 + 1  # supposedly expensive calculation

★★★★★★★★★★★★★★★★★.@cache@property(일부러)

속성에서 캐시를 사용하는 것을 지연 평가라고 합니다.kids.cache훨씬 더 많은 작업을 수행할 수 있습니다(인수, 속성, 메서드 유형, 클래스까지 모두 사용할 수 있습니다). 사용자의, " " " 입니다.kids.cache는 「」를 서포트하고 있습니다.cachetoolspython 2 python 3 (LRU, LFU, TTL, RR) 입니다.

중요: 기본 캐시 저장소kids.cache받아쓰다이것은 캐싱 스토어가 계속 증가하기 때문에 쿼리가 다른 장시간 실행 프로그램에는 권장되지 않습니다.예를 를 캐시 스토어에 수 있습니다.@cache(use=cachetools.LRUCache(maxsize=2))

'게으른 부동산 평가'라는 딱 맞는 이름을 찾고 싶어서요

나도 이걸 많이 해; 아마도 언젠가 내 코드에 그 레시피를 사용할거야.

Python Wiki에는 메모 데코레이터의 또 다른 예가 있습니다.

http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize

이 예에서는 파라미터를 변경할 수 있는 경우 결과를 캐시하지 않기 때문에 조금 스마트합니다.(이 코드를 체크해 주세요.매우 심플하고 흥미롭습니다!

Framework를 사용하는 Django Framework를 사용하여 뷰 .@cache_page(time)을 사용하다

예:

@cache_page(60 * 15, cache="special_cache")
def my_view(request):
    ...

자세한 내용은 여기를 참조하십시오.

"Python 3 functools.lru_cache의 C 구현"이라는 fastcache가 있습니다.표준 라이브러리보다 10배~30배 고속화"

선택한 답변과 같으며 Import만 다릅니다.

from fastcache import lru_cache
@lru_cache(maxsize=128, typed=False)
def f(a, b):
    pass

또한 설치가 필요한 펑툴과는 달리 아나콘다에 설치되어 있습니다.

joblib https://joblib.readthedocs.io/en/latest/memory.html 를 사용해 보세요.

from joblib import Memory
memory = Memory(cachedir=cachedir, verbose=0)
@memory.cache
    def f(x):
        print('Running f(%s)' % x)
        return x

메모라이즈 예시와 함께 다음과 같은 python 패키지를 발견했습니다.

  • cachepy; 캐시된 함수의 ttl 및 콜 수를 설정할 수 있습니다.또한 암호화된 파일 기반 캐시를 사용할 수 있습니다.
  • 퍼캐시

@lru_cache 애트리뷰트에는 하지 않습니다.

친구@mem★★★★★★★★★★★★★★★★★★:

import inspect
from copy import deepcopy
from functools import lru_cache, wraps
from typing import Any, Callable, Dict, Iterable


# helper
def get_all_kwargs_values(f: Callable, kwargs: Dict[str, Any]) -> Iterable[Any]:
    default_kwargs = {
        k: v.default
        for k, v in inspect.signature(f).parameters.items()
        if v.default is not inspect.Parameter.empty
    }

    all_kwargs = deepcopy(default_kwargs)
    all_kwargs.update(kwargs)

    for key in sorted(all_kwargs.keys()):
        yield all_kwargs[key]


# the best decorator
def mem(func: Callable) -> Callable:
    cache = dict()

    @wraps(func)
    def wrapper(*args, **kwargs) -> Any:
        all_kwargs_values = get_all_kwargs_values(func, kwargs)
        params = (*args, *all_kwargs_values)
        _hash = hash(params)

        if _hash not in cache:
            cache[_hash] = func(*args, **kwargs)

        return cache[_hash]

    return wrapper


# some logic
def counter(*args) -> int:
    print(f'* not_cached:', end='\t')
    return sum(args)


@mem
def check_mem(a, *args, z=10) -> int:
    return counter(a, *args, z)


@lru_cache
def check_lru(a, *args, z=10) -> int:
    return counter(a, *args, z)


def test(func) -> None:
    print(f'\nTest {func.__name__}:')

    print('*', func(1, 2, 3, 4, 5))
    print('*', func(1, 2, 3, 4, 5))
    print('*', func(1, 2, 3, 4, 5, z=6))
    print('*', func(1, 2, 3, 4, 5, z=6))
    print('*', func(1))
    print('*', func(1, z=10))


def main():
    test(check_mem)
    test(check_lru)


if __name__ == '__main__':
    main()

출력:

Test check_mem:
* not_cached:   * 25
* 25
* not_cached:   * 21
* 21
* not_cached:   * 11
* 11

Test check_lru:
* not_cached:   * 25
* 25
* not_cached:   * 21
* 21
* not_cached:   * 11
* not_cached:   * 11

peak을 persion에 사용하고 sha1을 짧은 고유 ID에 사용합니다.기본적으로 캐시는 함수의 코드와 인수 히스토를 해시하여 sha1을 얻은 후 이름에 해당 sha1이 있는 파일을 찾습니다.존재하는 경우 해당 함수를 열어 결과를 반환하고, 그렇지 않은 경우 함수를 호출하여 결과를 저장합니다(옵션으로 처리에 일정 시간이 걸린 경우에만 저장).

하지만, 맹세컨데, 이런 일을 할 수 있는 기존 모듈을 찾았고, 그 모듈을 찾으려 하고 있는 나를 발견했어요.가장 가까운 것은 http://chase-seibert.github.io/blog/2011/11/23/pythondjango-disk-based-caching-decorator.html 입니다.

유일한 문제는 해시 스트링(arg)이 거대 어레이에 고유하지 않기 때문에 대규모 입력에서는 잘 작동하지 않는다는 것입니다.

클래스에 콘텐츠의 안전한 해시를 반환하는 unique_hash() 프로토콜이 있으면 좋겠습니다.기본적으로는 제가 좋아하는 타입에 맞게 수동으로 구현했습니다.

Django를 사용하여 뷰를 캐시하려면 Nikhil Kumar의 답변을 참조하십시오.


단, 임의의 함수 결과를 캐시하려면 django-cache-utils를 사용할 수 있습니다.

Django 캐시를 재사용하여 사용하기 편리함cached데코레이터:

from cache_utils.decorators import cached

@cached(60)
def foo(x, y=0):
    print 'foo is called'
    return x+y

기능 캐시 심플 솔루션

ttl(time to life) 및 max_discl을 사용하여

  • 장식된 함수가 캐시할 수 없는 유형을 입력으로 사용할 경우 작동하지 않습니다(예: dicts).
  • 옵션 파라미터: ttl(모든 엔트리의 존속가능시간)
  • 옵션 파라미터: max_parameter(캐시 인수의 조합이 너무 많아 스토리지를 복잡하게 하지 않는 경우)
  • 기능에 중요한 부작용이 없는지 확인하다

사용 예

import time

@cache(ttl=timedelta(minutes=3), max_entries=300)
def add(a, b):
    time.sleep(2)
    return a + b

@cache()
def substract(a, b):
    time.sleep(2)
    return a - b

a = 5
# function is called with argument combinations the first time -> it takes some time
for i in range(5):
    print(add(a, i))

# function is called with same arguments again? -> will answer from cache
for i in range(5):
    print(add(a, i))

데코레이터 코드 복사

from datetime import datetime, timedelta

def cache(**kwargs):
  def decorator(function):
    # static function variable for cache, lazy initialization
    try: function.cache
    except: function.cache = {}
    def wrapper(*args):
        # if nothing valid in cache, insert something
        if not args in function.cache or datetime.now() > function.cache[args]['expiry']:
            if 'max_entries' in kwargs:
                max_entries = kwargs['max_entries']
                if max_entries != None and len(function.cache) >= max_entries:
                    now = datetime.now()
                    # delete the the first expired entry that can be found (lazy deletion)
                    for key in function.cache:
                        if function.cache[key]['expiry'] < now:
                            del function.cache[key]
                            break
                    # if nothing is expired that is deletable, delete the first
                    if len(function.cache) >= max_entries:
                        del function.cache[next(iter(function.cache))]
            function.cache[args] = {'result': function(*args), 'expiry': datetime.max if 'ttl' not in kwargs else datetime.now() + kwargs['ttl']}

        # answer from cache
        return function.cache[args]['result']
    return wrapper
  return decorator
from functools import wraps


def cache(maxsize=128):
    cache = {}

    def decorator(func):
        @wraps(func)
        def inner(*args, no_cache=False, **kwargs):
            if no_cache:
                return func(*args, **kwargs)

            key_base = "_".join(str(x) for x in args)
            key_end = "_".join(f"{k}:{v}" for k, v in kwargs.items())
            key = f"{key_base}-{key_end}"

            if key in cache:
                return cache[key]

            res = func(*args, **kwargs)

            if len(cache) > maxsize:
                del cache[list(cache.keys())[0]]
                cache[key] = res

            return res

        return inner

    return decorator


def async_cache(maxsize=128):
    cache = {}

    def decorator(func):
        @wraps(func)
        async def inner(*args, no_cache=False, **kwargs):
            if no_cache:
                return await func(*args, **kwargs)

            key_base = "_".join(str(x) for x in args)
            key_end = "_".join(f"{k}:{v}" for k, v in kwargs.items())
            key = f"{key_base}-{key_end}"

            if key in cache:
                return cache[key]

            res = await func(*args, **kwargs)

            if len(cache) > maxsize:
                del cache[list(cache.keys())[0]]
                cache[key] = res

            return res

        return inner

    return decorator

사용 예

import asyncio
import aiohttp


# Removes the aiohttp ClientSession instance warning.
class HTTPSession(aiohttp.ClientSession):
    """ Abstract class for aiohttp. """
    
    def __init__(self, loop=None) -> None:
        super().__init__(loop=loop or asyncio.get_event_loop())

    def __del__(self) -> None:
        if not self.closed:
            self.loop.run_until_complete(self.close())
            self.loop.close()
 

        return 
       

            

session = HTTPSession()

@async_cache()
async def query(url, method="get", res_method="text", *args, **kwargs):
    async with getattr(session, method.lower())(url, *args, **kwargs) as res:
        return await getattr(res, res_method)()


async def get(url, *args, **kwargs):
    return await query(url, "get", *args, **kwargs)
 

async def post(url, *args, **kwargs):
    return await query(url, "post", *args, **kwargs)

async def delete(url, *args, **kwargs):
    return await query(url, "delete", *args, **kwargs)

언급URL : https://stackoverflow.com/questions/815110/is-there-a-decorator-to-simply-cache-function-return-values

반응형