繁体   English   中英

在python中使用时间限制缓存内存

[英]Caching in-memory with a time limit in python

我有一个返回列表的函数,例如list_x。

def result(val):
    ..
    return(list_x)

我每分钟都调用result()并存储列表。

def other_func():
    #called every minute
    new_list = result(val)

我想将new_list的值存储一个小时(在某种形式的内存缓存中?),然后再次对其进行更新,基本上是一个小时而不是每隔一分钟调用一次result()。我读到functools.lru_cache但是我认为这无济于事。 有任何想法吗?

装饰者通常可以很好地解决这个问题

def cache(fn=None,time_to_live=3600*24): # one DAY default (or whatever)
    if not fn: return functools.partial(cache,time_to_live=time_to_live)
    my_cache = {}
    def _inner_fn(*args,**kwargs)
        kws = sorted(kwargs.items()) # in python3.6+ you dont need sorted
        key = tuple(args)+tuple(kw) 
        if key not in my_cache or time.time() > my_cache[key]['expires']:
               my_cache[key] = {"value":fn(*args,**kwargs),"expires":time.time()+ time_to_live}
        return my_cache[key]
    return __inner_fn

@cache(time_to_live=3600) # an hour
def my_sqrt(x):
    return x**0.5

@cache(time_to_live=60*30) # 30 mins
def get_new_emails():
    return my_stmp.get_email_count()

顺便说一句,这是内置在内存缓存中的,这可能是一个更好的解决方案(不确定所使用的问题域)

您还可以使用嵌套函数

def cache(time_to_live=3600*24): # one DAY default (or whatever)
    def _wrap(fn):
        my_cache = {}
        def _inner_fn(*args,**kwargs)
            kws = sorted(kwargs.items()) # in python3.6+ you dont need sorted
            key = tuple(args)+tuple(kw) 
            if key not in my_cache or time.time() > my_cache[key]['expires']:
                 my_cache[key] = {"value":fn(*args,**kwargs),"expires":time.time()+ time_to_live}
            return my_cache[key]
         return _inner_fn
    return _wrap

构建一个具有生存时间的单元素缓存非常简单:

_last_result_time = None
_last_result_value = None
def result(val):
    global _last_result_time
    global _last_result_value
    now = datetime.datetime.now()
    if not _last_result_time or now - _last_result_time > datetime.timedelta(hours=1):
        _last_result_value = <expensive computation here>
        _last_result_time = now
    return _last_result_value

如果您想将其概括为装饰器,则难度并不大:

def cache(ttl=datetime.timedelta(hours=1)):
    def wrap(func):
        time, value = None, None
        @functools.wraps(func)
        def wrapped(*args, **kw):
            nonlocal time
            nonlocal value
            now = datetime.datetime.now()
            if not time or now - time > ttl:
                value = func(*args, **kw)
                time = now
            return value
        return wrapped
    return wrap

如果您希望它处理不同的参数,请为每个参数存储一个生存时间:

def cache(ttl=datetime.timedelta(hours=1)):
    def wrap(func):
        cache = {}
        @functools.wraps(func)
        def wrapped(*args, **kw):
            now = datetime.datetime.now()
            # see lru_cache for fancier alternatives
            key = tuple(args), frozenset(kw.items()) 
            if key not in cache or now - cache[key][0] > ttl:
                value = func(*args, **kw)
                cache[key] = (now, value)
            return cache[key][1]
        return wrapped
    return wrap

当然,您可以为其添加关键功能-根据存储时间或LRU或您想要的任何其他方式,为它提供最大大小并逐出,将缓存统计信息公开为修饰函数上的属性,等等。在stdlib中实现lru_cache应该帮助您展示如何做大多数棘手的事情(因为它几乎完成了所有这些工作)。

创建一个充当缓存的函数,我们将其result_cacher

import time 
lastResultCache = 0 
resultCache = None
def result_cacher():
    if time.time() - lastResultCache >= 3600: #Checks if 3600 sec (1 hour) has passed since the last cache 
        lastResultCache = time.time()
        resultCache = result()
    return resultCache 

此函数检查一个小时是否已过去,如果已过,则更新缓存,然后返回缓存。

如果要对每个单独的输入而不是在每次调用函数时都应用缓存,请对lastResultCacheresultCache使用字典。

import time 
lastResultCache = {}
resultCache = {}
def result_cacher(val):
    #.get() gets value for key from dict, but if the key is not in the dict, it returns 0
    if time.time() - lastResultCache.get(val, 0) >= 3600: #Checks if 3600 sec (1 hour) has passed since the last cache 
        lastResultCache[val] = time.time()
        resultCache[val] = result(val)
    return resultCache.get(val)

cachetools==3.1.0ttl_cache装饰器的工作原理与functools.lru_cache类似,但是有一定的生存时间

import cachetools.func

@cachetools.func.ttl_cache(maxsize=128, ttl=10 * 60)
def example_function(key):
    return get_expensively_computed_value(key)


class ExampleClass:
    EXP = 2

    @classmethod
    @cachetools.func.ttl_cache()
    def example_classmethod(cls, i):
        return i* cls.EXP

    @staticmethod
    @cachetools.func.ttl_cache()
    def example_staticmethod(i):
        return i * 3

使用ring的解决方案

@ring.lru(expire=60*60)  # seconds
def cached_function(keys):
    return ...

如果您不需要LRU政策,

@ring.dict(expire=60*60)  # seconds
def cached_function(keys):
    return ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM