Jupyter(Python)中无法使用Cache原理分析
前言
最近需要在Jupyter中写一个类库,其中有一个文件实现从数据库中读取空间数据并加载为Feature对象,Feature对象是cartopy封装的geometry列表,能够方便的用于作图等。因为有很多数据是经常用到的,所以就写了很多常量将数据事先读好供用户直接调用,这样造成的一个问题是每次加载该页面的时候很慢,于是我就考虑可以写个Cache来缓存这些数据,这在其他情况下是再正常不过的需求,然而我却在这里折腾半天,踏了坑,坑里还有水,再也没有出来。。。在这里我简单分析一下失败的原因,如果有人有能解决的方案或者我有什么说的不对的地方,欢迎批评指导!
折腾过程
首先我考虑这个应该是写个Cache类,其中加入一个字典,于是找到了这样一块代码,初步看了一下代码没有问题,于是Copy上:
#coding=utf-8
from time import time
class Cache:
'''简单的缓存系统'''
def __init__(self):
'''初始化'''
self.mem = {}
self.time = {}
def set(self, key, data, age=-1):
'''保存键为key的值,时间位age'''
self.mem[key] = data
if age == -1:
self.time[key] = -1
else:
self.time[key] = time() + age
return True
def get(self,key):
'''获取键key对应的值'''
if key in self.mem.keys():
if self.time[key] == -1 or self.time[key] > time():
return self.mem[key]
else:
self.delete(key)
return None
else:
return None
def delete(self,key):
'''删除键为key的条目'''
del self.mem[key]
del self.time[key]
return True
def clear(self):
'''清空所有缓存'''
self.mem.clear()
self.time.clear()
很清晰的一段代码,并且加入了缓存时间,应当能满足我的要求的,在此页面定义了一个变量,创建一个FEATURE_CACHE对象如下:
FEATURE_CACHE = Cache()
这样我在需要缓存的页面只要先判断是否在缓存内,是则直接读取,否则使用原来的逻辑读取数据库并存入缓存即可,改造如下:
if FEATURE_CACHE.get(ds_id) != None:
return FEATURE_CACHE.get(ds_id)
else:
...
geo_feature = ...
FEATURE_CACHE.set(ds_id, geo_feature)
return geo_feature
逻辑上清晰易懂,然后尝试调用。新建一个jupyter页面,多次调用,很好,只有第一次比较慢,再次调用就非常快,本以为这就解决了问题,我也是灵光一闪,既然我是全局缓存那就再开一个页面试试吧,于是又新建了一个jupyter页面,大跌眼镜的事情出现了,居然也是第一次调用非常慢,这是什么逻辑,为什么这里面没有缓存。然后经历了无数次加输出信息调试、重启kernel调试、staticmethod方法、单例等均达不到效果,单例的代码如下:
class Cache:
__instance = None
__lock = threading.Lock() # used to synchronize code
mem = {}
time = {}
def __init__(self):
"disable the __init__ method"
'''简单的缓存系统'''
def set(self, key, data, age=-1):
'''保存键为key的值,时间位age'''
self.mem[key] = data
if age == -1:
self.time[key] = -1
else:
self.time[key] = time() + age
return True
def get(self,key):
'''获取键key对应的值'''
if key in self.mem.keys():
if self.time[key] == -1 or self.time[key] > time():
return self.mem[key]
else:
self.delete(key)
return None
else:
return None
def delete(self,key):
'''删除键为key的条目'''
del self.mem[key]
del self.time[key]
return True
def clear(self):
'''清空所有缓存'''
self.mem.clear()
self.time.clear()
@staticmethod
def getInstance():
if not Cache.__instance:
Cache.__lock.acquire()
if not Cache.__instance:
Cache.__instance = object.__new__(Cache)
object.__init__(Cache.__instance)
Cache.__lock.release()
return Cache.__instance
这样就是不再创建Cache的实例,而是直接调用Cache.getInstance()。可想而知这样也是不行的。于是折腾一番后我得出这么一个结果。
结果与原理
当我们在一个jupyter页面中调用某个python库的时候,只要在这个jupyter页面中不重新启动内核,则已经加载过的模块会自动缓存(是python的缓存,并非我写的缓存),重启内核相当于打开一个新的jupyter页面,并且在重新打开一个jupyter页面时,即使其他jupyter页面已经加载过了相应的调用,也不会缓存,会再次去执行程序,这样我写的Cache类就没有用了。所以结论就是在jupyter中我的Cahce缓存类加不加效果是一样的。那么原理是什么呢?
其实很简单,只是我刚开始对python的运行机理和生命周期等不太熟悉,才走了这个弯路,折腾一番大概明白了。首先普通的python程序使用python xx.py启动的时候这样写Cahce肯定是可行的,能够实现全局缓存,因为这是在一个application内部,加载过的python文件会编译成pyc,再次加载的时候会直接调用此pyc而不会重新执行,并且整体是共享内存的。而在jupyter中每一个jupyter页面都相当于启动了一个application,所以他们相互之间是隔离的,即无法共享pyc文件,也无法共享内存,于是重新打开一个jupyter页面就是一个新的Cache,这样写不写Cache得到的结果是一致的。
总结
当然可以考虑采用文件缓存的方式,即首次读取的时候将数据库内容加载到本地文件,再次调用的时候读取文件,然而并没有尝试这样会快多少,并且本身访问量就不大,数据库是完全能抗住的,于是不知道这样的缓存有多少意义。当然也可以使用redis、memcache等缓存件,但是这样就整大发了,没必要使用jupyter了吧。以上是我对此问题的个人见解,欢迎大家提出宝贵意见,不甚感激!
Jupyter(Python)中无法使用Cache原理分析的更多相关文章
- Python中的浮点数原理与运算分析
Python中的浮点数原理与运算分析 本文实例讲述了Python中的浮点数原理与运算.分享给大家供大家参考,具体如下: 先看一个违反直觉的例子: >>> s = 0. > ...
- Python 中生成器的原理
生成器的使用 在 Python 中,如果一个函数定义的内部使用了 yield 关键字,那么在执行函数的时候返回的是一个生成器,而不是常规函数的返回值. 我们先来看一个常规函数的定义,下面的函数 f() ...
- Guava Cache 原理分析与最佳实践
前言 目前大部分互联网架构 Cache 已经成为了必可不少的一环.常用的方案有大家熟知的 NoSQL 数据库(Redis.Memcached),也有大量的进程内缓存比如 EhCache .Guava ...
- MySQL的Query Cache原理分析
QueryCache(下面简称QC)是根据SQL语句来cache的.一个SQL查询如果以select开头,那么MySQL服务器将尝试对其使用QC.每个Cache都是以SQL文本作为key来存的. 原理 ...
- python中fork()函数生成子进程分析
python的os module中有fork()函数用于生成子进程,生成的子进程是父进程的镜像,但是它们有各自的地址空间,子进程复制一份父进程内存给自己,两个进程之 间的执行是相互独立的,其执行顺序可 ...
- Java中的递归原理分析
解释:程序调用自身的编程技巧叫做递归. 程序调用自身的编程技巧称为递归( recursion).递归做为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用 ...
- Python中关于XML-RPC原理
SimpleXMLRPCServer模块为XML-RPC服务端的写入提供了一个基本的框架.利用SimpleXMLRPCServer服务器既可以一直空闲,也可以利用CGIXMLRPCRequestHan ...
- python中fork()函数生成子进程分析-乾颐堂
python的os module中有fork()函数用于生成子进程,生成的子进程是父进程的镜像,但是它们有各自的地址空间,子进程复制一份父进程内存给自己,两个进程之 间的执行是相互独立的,其执行顺序可 ...
- Python中闭包的原理
定义: 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 简单闭包的例子: 下面是一个使用闭包简单的例子,模拟一个计数器,通过将 ...
随机推荐
- Android笔记: 日期格式化
下面的转换符来自Java,但是在android中同样可用.(以下表格内容来自互联网.) 常见日期格式化转换符 转换符 说 明 示 例 %te 一个月中的某一天(1-31) 2 %tb 指定语言环 ...
- javascript基础进阶——执行环境及作用域链
概念 执行环境 执行环境定义了变量或函数有权访问的其他函数,决定了他们各自的行为.每个执行环境都有一个与之关联的变量对象. 变量对象 环境中定义的所有变量和函数都保存在这个对象中. 全局执行环境 全局 ...
- 函数作用域中的this问题
首先一起回顾下预解析和作用域吧: 预解析: 浏览器每读到一个script标签或function,先不执行任何代码,会先把整个代码快速的浏览一遍,然后从中 挑出 var 和 function两个关键字 ...
- Mybatis --- 映射文件、参数处理、参数值的获取、select元素
映射文件:指导着MyBatis如何进行数据库增删改查, 有着非常重要的意义: - cache 命名空间的二级缓存配置 - cache-ref 其他命名空间缓存配置的引用. - result ...
- MySQL(二)之服务管理与初始化文件修改和连接MySQL
上一篇给大家介绍了怎么在linux和windows中安装mysql,本来是可以放在首页的,但是博客园说“安装配置类文件”不让放在首页.接下来给大家介绍一下在linux和windows下MySQL的一下 ...
- 利用wireshark任意获取qq好友IP实施精准定位
没事玩一把,感觉还挺有趣,首先打开wireshark: 不管你连接的什么网,如图我连接的是WLAN,双击进入如图界面: ctrl-f进行搜索:如图 选择分组详情,字符串,并输入020048.这时候你就 ...
- JS源生代码“增删改查”之增
51呢最近在做一个管理数据的,第一次接触到用JS的源代码去实现一些功能,才知道网页里的许多功能都是依赖于“增删改查”完成的,下面的几张图片就是对于增的演示: 下面是有关HTML的代码:这个主要是弹窗部 ...
- ABP 框架学习-01篇
从来没有自己写过太多的技术性文章,博客里面的文章都是拷贝别人的东西,做一个笔记功能给自己用的.最近觉得应该写点自己的学习博客 https://aspnetboilerplate.com/ ABP框架, ...
- 自己动手写fullPage插件
仿造fullPage.js https://alvarotrigo.com/fullPage/#firstPage 自己参照网上教程写了一个,加了注释.主要是练习造轮子的能力,需求是不断变化的只拿来用 ...
- 一、Solr的相关概念了解
1.1. 什么是Solr Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器.Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置.可扩展 ...