python-web: 缓存实现

缓存的原因

缓存是一种将定量数据加以保存以备迎合后续请求的处理方式,旨在加快数据的检索速度。通过缓存机制将数据放到字典中,如果下次需要的时候可以直接到字典中获取。

实现一个缓存类并测试

class MyCache(object):
    def __init__(self):
        self.cache = {}
        self.max_cache_size = 10

    def __contains__(self, key):
        """
        判断该键是否存在于缓存当中返回 True 或者 False
        :param key:
        :return:
        """
        return key in self.cache

    def update(self, key, value):
        """
        更新该缓存字典 您可能选择性删除
        :param key:
        :param value:
        :return:
        """
        if key not in self.cache and len(self.cache) >= self.max_cache_size:
            self.remove_oldest()
        self.cache[key] = {'date_accessed': datetime.datetime.now(), "value": value}

    def remove_oldest(self):
        """
        删除备份最早访问日期的输入数据
        :return:
        """
        oldest_entry = None
        # 根据其中的 date_accessed 字段找到最早的一个任务
        for key in self.cache:
            if oldest_entry is None:
                oldest_entry = key
            elif self.cache[key]["date_accessed"] < self.cache[oldest_entry]['date_accessed']:
                oldest_entry = key
        self.cache.pop(oldest_entry)

    @property
    def size(self):
        """
        返回缓存的容量大小
        :return:
        """
        return len(self.cache)


if __name__ == "__main__":
    # 测试缓存类
    keys = ['test', 'red', 'fox', 'fence', 'junk', 'other', 'alpha', 'bravo', 'cal', 'devo', 'ele']
    s = "abcdefghijklmnop"
    cache = MyCache()
    for i, key in enumerate(keys):
        if key in cache:
            continue
        else:
            value = ".".join([random.choice(s) for i in range(20)])
            cache.update(key, value)
        print(i+1, "------> ", cache.size)

使用 lru_cache 装饰器

在 python3.2 中 引入了一个非常优雅的缓存机器,即 functool 模块中的 lru_cache 装饰器
@functools.lru_cache(maxsize=None, typed=False)
使用functools模块的lur_cache装饰器,可以缓存最多 maxsize 个此函数的调用结果,从而提高程序执行的效率,
特别适合于耗时的函数。参数maxsize为最多缓存的次数,如果为None,则无限制,设置为2n时,性能最佳;
如果 typed=True(注意,在 functools32 中没有此参数),则不同参数类型的调用将分别缓存,例如 f(3) 和 f(3.0)。
被 lru_cache 装饰的函数会有 cache_clear 和 cache_info 两个方法,分别用于清除缓存和查看缓存信息。
这里用一个简单的示例演示 lru_cache 效果:
注: 有一个用 C 实现的,更快的,同时兼容 Python2 和 Python3 的第三方模块 fastcache 能够实现同样的功能。

from functools import lru_cache


@lru_cache(None)
def add(x, y):
    print(f"calculating: {x} + {y}")
    return x+y


print(add(1, 2))
print(add.cache_info())   # 显示缓存信息
add.cache_clear()   # 清空缓存
# 在第二次执行的时候,并没有真正执行函数体, 二是直接返回缓存的结果
print(add(1, 2))
print(add(2, 3))

web 缓存

在web 程序中更常用的做法是实现一个自己的redis客户端封装,并且通过不同的前缀实现不同类别数据的缓存:
代码如下:

# 测试Python-redis的缓存功能
import json
import pprint

from redis import Redis

# from flask_restful import current_app

myredis = Redis.from_url("redis://localhost:6379/1", db=0)


class RedisCache(object):
    def __init__(self, key_prefix="", encoder=None, decoder=None):
        """
        初始化 redis 存储对象
        :param key_prefix:  前缀
        :param encoder: 编码
        :param decoder: 解码
        """
        self._key_prefix = key_prefix
        # self._timeout = timeout
        self.encoder = encoder
        self.decoder = decoder
        self._r = None

    @property
    def rds(self):
        """
        获取一个底层的 redis 对象
        :return:
        """
        if self._r is not None:
            return self._r
        self._r = myredis
        return self._r

        # if getattr(current_app, "redis", None):
        #     self._r = current_app.redis
        # else:
        #     rds = Redis.from_url(current_app.config["REDIS_URI"], db=0)
        #     current_app.redis = rds
        #     self._r = rds
        # return self._r

    def key(self, key):
        """
        给 key 加上前缀
        :param key:
        :return:
        """
        return f"{self._key_prefix}{key}"

    def cache(self, key, value, timeout=None):
        """
        存入
        :param key:
        :param value:
        :param timeout:
        :return:
        """
        if self.encoder:
            value = self.encoder(value)
        # print(f"cache: {self.key(key)} {value}")
        if timeout is not None:
            self.rds.set(self.key(key), value, timeout)
        else:
            self.rds.set(self.key(key), value)

    def get(self, key):
        """
        取出
        :param key:
        :return:
        """
        key = self.key(key)
        value = self.rds.get(key)
        if (value is not None) and self.decoder:
            value = self.decoder(value)
        return value

    def delete(self, key):
        """
        删除
        :param key:
        :return:
        """
        self.rds.delete(self.key(key))

    def get_keys(self, *keys):
        """
        获取已存储键值
        :param keys:
        :return:
        """
        pl = self.rds.pipeline()
        for key in keys:
            pl.get(self.key(key))

        return pl.execute()


forked_infos = [
    {"annualized_returns": None,
     "create_time": 1562038393,
     "desc": "失败的MACD策略",
     "forked_id": "None",
     "max_drawdown": "---",
     "origin": None,
     "parent": None
     },
    {"annualized_returns": None,
     "create_time": 1562060612,
     "desc": "失败的MACD策略",
     "forked_id": "5d1b2744b264566d3f3f3632",
     "max_drawdown": None,
     "origin": "5d1ad079e86117f3883f361e",
     "parent": "5d1ad079e86117f3883f361e"
     }
]

mycache = RedisCache("DEMO", encoder=json.dumps, decoder=json.loads)
# Invalid input of type: 'list'. Convert to a byte, string or number first.
mycache.cache("5d1ad079e86117f3883f361e", forked_infos)

print(pprint.pformat(mycache.get("5d1ad079e86117f3883f361e")))

链接:

http://landcareweb.com/questions/35613/ru-he-zai-python-functools-lru-cacheshang-shi-yong-cache-clear

https://hugoren.iteye.com/blog/2378567

猜你喜欢

转载自blog.csdn.net/Enjolras_fuu/article/details/94727163