带过期的lru缓存【python】


import threading
import functools
import time


class link_dict(dict):
    __slots__ = ['_pre', '_nxt']

    def __init__(self):
        self._pre = {
    
    None: None}
        self._nxt = {
    
    None: None}

    def __setitem__(self, k, v) -> None:
        if k == None:
            raise ValueError('Key cannot be None.')
        super().__setitem__(k, v)
        self._add_link(k, None)

    def __delitem__(self, k) -> None:
        if k == None:
            raise ValueError('Key cannot be None.')
        super().__delitem__(k)
        self._del_link(k)

    def _del_link(self, k):
        pre, nxt = self._pre[k], self._nxt[k]
        del self._pre[k], self._nxt[k]
        self._nxt[pre] = nxt
        self._pre[nxt] = pre

    def _add_link(self, k, pos):
        if k in self._pre:
            self._del_link(k)
        pre, nxt = self._pre[pos], pos
        self._pre[nxt] = self._nxt[pre] = k
        self._pre[k] = pre
        self._nxt[k] = nxt

    @property
    def head(self):
        return self._nxt[None]

    @property
    def tail(self):
        return self._pre[None]

    def __iter__(self):
        k = self.head
        while k != None:
            yield self[k]
            k = self._nxt[k]

    def __reversed__(self):
        k = self.tail
        while k != None:
            yield self[k]
            k = self._pre[k]


def make_key(
    args: tuple,
    kwds: dict,
):
    key = args
    if kwds:
        key += ('#kwd_mark#', )
        for item in kwds.items():
            key += item
    elif len(key) == 1 and type(key[0]) in {
    
    int, str}:
        return key[0]

    class _HashedSeq(list):
        __slots__ = 'hashvalue'

        def __init__(self, tup, hash=hash):
            self[:] = tup
            self.hashvalue = hash(tup)

        def __hash__(self):
            return self.hashvalue

    return _HashedSeq(key)


def stamped_cache(*, duration=0, maxsize=128, get_key=make_key):
    if duration <= 0:
        return lambda func: functools.lru_cache(maxsize=maxsize)(func)
    last = link_dict()
    lock = threading.RLock()

    def _cache_wrapper(func):
        @functools.lru_cache(maxsize=maxsize)
        def func_with_stamp(*args, **kwargs):
            return func(*args[:-1], **kwargs)

        @functools.wraps(func)
        def _func(*args, **kwargs):
            nonlocal last, lock
            k = get_key(args, kwargs)
            nt = time.time()
            with lock:
                lt = last.get(k)
                if lt == None or nt - lt >= duration:
                    last[k] = nt
                if len(last) > maxsize:
                    del last[last.head]
            args += (last[k], )
            return func_with_stamp(*args, **kwargs)

        return _func

    return _cache_wrapper


 

注:link_dict 可以用 collections.OrderedDict 替代。

写法二,用OrderedDict


import threading
import functools
import collections
import time
import inspect


def make_key(func, *args, **kwargs):
    class _HashedSeq(list):
        __slots__ = 'hashvalue'

        def __init__(self, tup, hash=hash):
            self[:] = tup
            self.hashvalue = hash(tup)

        def __hash__(self):
            return self.hashvalue

    sig = inspect.signature(func)
    pos_args, kw_args = (), {
    
    }
    for p, v in zip(sig.parameters.values(), args):
        if p.kind == inspect.Parameter.POSITIONAL_ONLY:
            pos_args += (v, )
        else:
            kw_args[p.name] = v
    kw_args.update(kwargs)
    key = pos_args[:]
    for kv in sorted(kw_args.items()):
        key += kv
    if len(key) == 1 and type(key[0]) in (str, int):
        return key[0], pos_args, kw_args
    return _HashedSeq(key), pos_args, kw_args


def stamped_cache(*, duration=0, maxsize=128):
    if duration <= 0:
        return lambda func: functools.lru_cache(maxsize=maxsize)(func)

    last = collections.OrderedDict()
    lock = threading.RLock()

    def _cache_wrapper(func):
        @functools.lru_cache(maxsize=maxsize)
        def func_with_stamp(*args, **kwargs):
            return func(*args[:-1], **kwargs)

        @functools.wraps(func)
        def _func(*args, **kwargs):
            nonlocal last, lock
            k, args, kwargs = make_key(func, *args, **kwargs)
            nt = time.time()
            with lock:
                if k in last:
                    if nt - last[k] >= duration:
                        last.move_to_end(k)
                        last[k] = nt
                else:
                    last[k] = nt
                    if len(last) > maxsize:
                        last.popitem(last=False)
                first = next(iter(last.items()))
                if nt - first[1] >= duration:
                    # 防止无效缓存一直存在
                    last.popitem(last=False)
            args += (last[k], )
            return func_with_stamp(*args, **kwargs)

        return _func

    return _cache_wrapper

猜你喜欢

转载自blog.csdn.net/qq_45256489/article/details/127937127
今日推荐