前言

最近需要在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原理分析的更多相关文章

  1. Python中的浮点数原理与运算分析

    Python中的浮点数原理与运算分析 本文实例讲述了Python中的浮点数原理与运算.分享给大家供大家参考,具体如下: 先看一个违反直觉的例子:     >>> s = 0. > ...

  2. Python 中生成器的原理

    生成器的使用 在 Python 中,如果一个函数定义的内部使用了 yield 关键字,那么在执行函数的时候返回的是一个生成器,而不是常规函数的返回值. 我们先来看一个常规函数的定义,下面的函数 f() ...

  3. Guava Cache 原理分析与最佳实践

    前言 目前大部分互联网架构 Cache 已经成为了必可不少的一环.常用的方案有大家熟知的 NoSQL 数据库(Redis.Memcached),也有大量的进程内缓存比如 EhCache .Guava ...

  4. MySQL的Query Cache原理分析

    QueryCache(下面简称QC)是根据SQL语句来cache的.一个SQL查询如果以select开头,那么MySQL服务器将尝试对其使用QC.每个Cache都是以SQL文本作为key来存的. 原理 ...

  5. python中fork()函数生成子进程分析

    python的os module中有fork()函数用于生成子进程,生成的子进程是父进程的镜像,但是它们有各自的地址空间,子进程复制一份父进程内存给自己,两个进程之 间的执行是相互独立的,其执行顺序可 ...

  6. Java中的递归原理分析

    解释:程序调用自身的编程技巧叫做递归.        程序调用自身的编程技巧称为递归( recursion).递归做为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用 ...

  7. Python中关于XML-RPC原理

    SimpleXMLRPCServer模块为XML-RPC服务端的写入提供了一个基本的框架.利用SimpleXMLRPCServer服务器既可以一直空闲,也可以利用CGIXMLRPCRequestHan ...

  8. python中fork()函数生成子进程分析-乾颐堂

    python的os module中有fork()函数用于生成子进程,生成的子进程是父进程的镜像,但是它们有各自的地址空间,子进程复制一份父进程内存给自己,两个进程之 间的执行是相互独立的,其执行顺序可 ...

  9. Python中闭包的原理

    定义: 如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure). 简单闭包的例子: 下面是一个使用闭包简单的例子,模拟一个计数器,通过将 ...

随机推荐

  1. BotVS开发基础—2.5 状态信息显示表格

    代码 import json def main(): #part 1 bol = True num = 10; # str = "ABC"; # 字符串 list = [1, 2, ...

  2. 一篇文章读懂Java类加载器

    Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下. 关于Java类加载器的知识,网上 ...

  3. C# 根据Excel模版导出文件

    string fileName = Path.Combine(Application.StartupPath, "kfwh_yhxf.xls"); Microsoft.Office ...

  4. INotifyPropertyChanged(监听数据),当数据改变时调用

    public class BaseViewModel : INotifyPropertyChanged    {        public event PropertyChangedEventHan ...

  5. Tomcat session集群

    author:JevonWei 版权声明:原创作品 环境 tomcatA 172.16.253.108 tomcatB 172.16.253.105 代理服务器 172.16.253.191 Tomc ...

  6. 程序员也是弱势群体?——从WePhone开发者事件说起

    作为一名不爱凑热闹的人,今天一直在持续关注一个热点事件--WePhone开发者自杀,即使前几天热议的孕妇跳楼新闻我都不太关注,但是这个事件却让我深深的震撼,花了几个小时在微博上搜索了相关的信息,去了解 ...

  7. HTTP协议Keep-Alive模式详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp22 HTTP协议Keep-Alive模式详解 1.什么是Keep-Aliv ...

  8. el表达式里面fn的用法

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp31 头部加入标签库 <%@ taglib prefix=" ...

  9. 【打死树莓派】-树莓派3代jessie+Opencv-解决安装不了libgtk2.0-dev包问题

    按照国际法先贴问题 Some packages could not be installed. This may mean that you have requested an impossible ...

  10. JAVA基础第三组(5道题)

    11 [程序11] 题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 1.程序分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去 掉不满足条件 ...