越来越觉得的缓存是计算机科学里最NB的发明(没有之一), 现在项目用的是redis做的缓存, 它的两个特性用的蛮顺手的:

键值查找功能

缓存可设置过期时间

突突然的,觉得用python也可以简单的模拟一下,做一个本地的轻量级缓存.(不过, 注意一点:redis的缓存可以用于分布式, python模拟的则不行, 但是如果把本地缓存的过期时间设的短一点,比如10s, 在大并发下还是有不错表现的)

对于键值查找功能, python原生的字典dict完美胜任. 对于缓存自动过期, 简单的想法后台起个服务, 定期检测, 但是这样代码会相对复杂,而且还要抢占宝贵的cpu资源,失去了轻量级的初衷.

那么思路是这样的:

只要将每个对应的缓存键值,加一个expire字段,代表过期时间点,

第一次获取, expire=当前时间点+过期时间

非第一次获取时, 通过expire判断是否已过期, 如果过期则可认为数据没找到,反之返回正常的缓存

简单的代码如下:

def get(self, key):

value = self._cache.get(key, self.notFound)

if(value is not self.notFound):

expire = value[r'expire']

if( self.nowTime() > expire):

return self.notFound

else:

return value

else:

return self.notFound

通过第1步, 基本功能已实现,现在收拾"烂摊子". 步骤1明显的确点就是内存泄漏了(搞python的应该不怎么听的到"内存泄漏"),原因很明显,通过字典模拟redis缓存, 所有数据都保存在缓存字典中(即使它过期了), 因为没人去删除它们.

解决的方法是,采用弱引用

python 文档有有句:

A primary use for weak references is to implement caches or mappings holding large objects, where it’s desired that a large object not be kept alive solely because it appears in a cache or mapping.

就是说,弱引用主要的用途也是为了实现缓存.

对于我们的应用,WeakValueDictionary这货就很符合需求,那么代码就如下这样了:

class LocalCache():

notFound = object()

class Dict(dict):

def __del__(self):

pass

def __init__(self, maxlen=2):

self.weak = weakref.WeakValueDictionary()

@staticmethod

def nowTime():

return int(time.time())

def get(self, key):

value = self.weak.get(key, self.notFound)

if(value is not self.notFound):

expire = value[r'expire']

if( self.nowTime() > expire):

return self.notFound

else:

return value

else:

return self.notFound

def set(self, key, value):

self.weak[key] = LocalCache.Dict(value)

几点说明下:

创建内部类Dict的原因

python文档的说法

Several built-in types such as list and dict do not directly support weak references but can add support through subclassing:

classDict(dict):

pass

obj=Dict(red=1,green=2,blue=3)

就是说内建的list和dict不能直接支持弱引用,但是继承他们的子类就支持弱引用了. so..

weakref.WeakValueDictionary说明

现在就是用WeakValueDictionary来代替之前的dict了,弱引用的优势就是:

WeakValueDictionary随时都可能被回收(听上去很不靠谱,不过下面有解决方法), 所以不用担心之前的内存泄漏问题.

可能有人担心,WeakValueDictionary只是value值是弱引用,也就说value可以被回收,但是key值回一直存在,导致泄漏,don't wrong, 有文档为证:

Entries in the dictionary will be discarded when no strong reference to the value exists any more.

即如果value值没有强引用了,那么对应的记录就会被丢弃(这句话有彩蛋). 所以也不用担心key导致的泄漏了.

当初写完这个后,感觉一切都比较的perfect. 但是还是too young too simple.

最后较成熟的方案

重新关注下第二步, 有彩蛋的那句话, 这句话再深入理解下就是,如果self.weak[key] = LocalCache.Dict(value)这样,LocalCache.Dict(value)没有其他强引用, 那么对不起,下一瞬间这个记录就没了(WTF).

所以之前的写法会导致--没有任何缓存作用(如果你耐着性子看到这,估计要骂娘了。。。),不过既然都写了这么多,方案还是有的(我在 http://stackoverflow.com 找到类似的方案,稍微改进了下),既然要强引用,那就给他强引用了,代码如下:

import weakref, collections

import time

class LocalCache():

notFound = object()

class Dict(dict):

def __del__(self):

pass

def __init__(self, maxlen=10):

self.weak = weakref.WeakValueDictionary()

self.strong = collections.deque(maxlen=maxlen)

@staticmethod

def nowTime():

return int(time.time())

def get(self, key):

value = self.weak.get(key, self.notFound)

if(value is not self.notFound):

expire = value[r'expire']

if( self.nowTime() > expire):

return self.notFound

else:

return value

else:

return self.notFound

def set(self, key, value):

self.weak[key] = strongRef = LocalCache.Dict(value)

self.strong.append(strongRef)

代码跟之前的差不多,就是多了self.strong这个队列来保存强引用, 并利用collections.deque的一个特性:

the deque is bounded to the specified maximum length. Once a bounded length deque is full, when new items are added, a corresponding number of items are discarded from the opposite end.

意思是如果给deque设置了大小(通过maxlen,不传或设为None则没有限制), 那么deque满的时候,新添加的对象会将之前的'老家伙挤出去'.

整个过程是这样的,刚开始往缓存加数据时, 添加的每一个值都有一个弱引用和强引用,不停的加,直到deque的队列满了(地主家也没余粮啊), 这时后面每加一个,都将导致deque中最早加入的强引用被deque废弃,而被废弃的强引用对应的值只有弱引用了,于是与之相关的WeakValueDictionary记录也被回收了(不知道有没人闪过虚拟内存中内存不够用时,数据在硬盘和内存捣鼓的画面)

到这基本已经写完。当然这个LocalCache类还有很多可完善的地方,这里只是讲解下它的形成过程。

附加一个应用这个LocalCache类的函数调用缓存装饰器

from functools import wraps

def funcCache(expire=0):

caches = LocalCache()

def _wrappend(func):

@wraps(func)

def __wrapped(*args , **kwargs):

#计算出缓存的key值

key = str(func) + str(args) + str(kwargs)

result = caches.get(key)

if(result is LocalCache.notFound):

result = func(*args, **kwargs)

caches.set(key, {r'result':result, r'expire':expire + caches.nowTime() })

result = caches.get(key)

return result

return __wrapped

return _wrappend

python使用redis做缓存_python实现类redis缓存相关推荐

  1. python中self做前缀_python 创建类和为什么类方法中self形参必不可少?

    我们观察到在类中定义方法时,都带有self形参,为何必须在方法中定义形参self呢? 因为Python调用方法创建类实例时,将自动传入实参self.每个与类相关联的方法调用都自动传递实参self,他是 ...

  2. python使用redis做缓存_Python中的Redis客户端缓存(二)

    Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 处理失效 无效消息如何发送到被追踪的客户端取决于客户端正在使用的Redis序列化协议(RESP).早期版本 ...

  3. python爬虫的硬件配置_python爬虫之redis环境简单部署

    Redis 简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久 ...

  4. python计算复数的辐角_Python 自定义类中的函数和运算符重载

    如果你曾在字符串(str)对象上进行过 + 或 * 运算,你一定注意到它跟整数或浮点数对象的行为差异: >>> # 加法 >>> 1 + 2 3 >>& ...

  5. python redis密码登录_Python远程连接Redis

    import redis r=redis.Redis(host='192.168.56.102',port=6379,db=0,password='jinxfredis' ) r.set('name' ...

  6. python redis 消息队列_python中利用redis构建任务队列(queue)

    Python中的使用标准queue模块就可以建立多进程使用的队列,但是使用redis和redis-queue(rq)模块使这一操作更加简单. Part 1. 比如首先我们使用队列来简单的储存数据:我们 ...

  7. python队列来做什么_python分布式爬虫中的消息队列是什么?

    当排队等待人数过多的时候,我们需要设置一个等待区防止秩序混乱,同时再有新来的想要排队也可以呆在这个地方.那么在python分布式爬虫中,消息队列就相当于这样的一个区域,爬虫要进入这个区域找寻自己想要的 ...

  8. python具体能做什么_python都能干嘛

    广告关闭 云服务器1核2G首年99年,还有多款热门云产品满足您的上云需求 python有很多优雅有趣的代码写法,同时还很简短,以至于当我刚开始接触这个编程语言的时候,就爱不释手. 而前几天的编程语言榜 ...

  9. python爬虫怎么做毕业设计_python语言爬虫做成毕业设计的话,怎样答辩演示,或者怎样把爬虫复杂化?...

    作为一名IT行业的从业者,同时也是一名计算机专业的教育工作者,我来回答一下这个问题. 对于计算机专业的学生来说,如果想把毕业设计定位在爬虫上,虽然从技术选型上是完全可以的,但是通过爬虫来获取数据本身还 ...

最新文章

  1. C - Catch That Cow POJ - 3278
  2. 致命错误:没有用于主机192.168.1.112,用户postgres
  3. VirtualBox安装Kali
  4. optee中TA的堆的分配
  5. linux获取性能指数,Linux环境获取系统性能数据
  6. 关于java中BufferedReader的read()及readLine()方法的使用心得
  7. SQL语言基础:SQL授权相关知识笔记​
  8. 【C++进阶】 遵循TDD原则,实现平面向量类(Vec2D)
  9. Nginx配置Thinkphp3.2.3配置,访问Nginx报 No input file specified. 的问题解决
  10. Unity 脚本的生命周期
  11. JavaScript库
  12. 2018年度中国LC3盛大召开!
  13. MIMO系统模型构建
  14. 数据库设计——概念模型
  15. eclipse项目修改后浏览器不更新
  16. 查询主表每条数对应的最新一条日志 group
  17. 行业务实派:解锁数据价值,翼方健数全栈隐私安全计算技术
  18. iOS内存管理——alloc/release/dealloc方法的GNUstep实现与Apple的实现
  19. kubeadm reset重新初始化过程
  20. 操作系统原理课程设计任务书

热门文章

  1. java excel导出 模板_Java Excel 导出 模板
  2. 7z 头部错误 数据错误_Vue项目组件数据类型错误处理
  3. 查看和设置tomcat内存
  4. linux结构体数组的定义数组,task_struct结构体中的run_list和array域
  5. java生产者消费者模型到精通_java生产者消费者模型
  6. python3.7.4安装pip_python3.7下pip的安装教程
  7. avue-crud 会多出来空白的一列
  8. div加border样式
  9. 引用类型 —— Array类型
  10. Docker学习笔记_安装和使用Zookeeper