数据类的简单sqlite支持的持久缓存
cachew的Python项目详细描述
cachew:quick namedtuple/dataclass缓存
tldr:cachew可以持久地缓存任何序列(an迭代器)在namedtuples或数据类到磁盘上的sqlite数据库中。 数据库模式是根据类型注释自动推断出来的(pep 526)。
它的工作方式与functools.lru cache rel="nofollow">functools.lru cache类似:缓存数据只是修饰它而已。
与functools.lru缓存的不同之处在于,程序运行之间会保留数据。
动机
我经常发现自己在处理大量的数据,计算其中的一些集合,或者只提取我感兴趣的部分。当我试图尽可能多地使用repl时,有些东西仍然很脆弱,而且在开发过程中经常需要重新运行整个过程。如果数据解析和处理只需几秒钟,在某些情况下更不用说几分钟,这可能会令人沮丧。
处理它的传统方法是序列化结果以及某种类型的输入文件散列(如md5)。 在下次运行时进行比较,如果没有更改,则返回缓存数据。
虽然听起来很简单,但每次你需要记住一些数据,用例行程序污染代码,分散你对主要任务的注意力时,这样做是相当乏味的。
示例
假设您正在为一些大型数据集(比如,从Wikipedia存档中提取URL及其标题)开发数据分析管道。
解析它(提取链接
函数)需要几个小时,但是,存档可能更新得不太频繁。
有了这个库,您可以通过一个@cachew
装饰器来实现它。
>>>fromtypingimportNamedTuple,Iterator>>>classLink(NamedTuple):...url:str...text:str...>>>@cachew...defextract_links(archive:str)->Iterator[Link]:...foriinrange(5):...importtime;time.sleep(1)# simulate slow IO...yieldLink(url=f'http://link{i}.org',text=f'text {i}')...>>>list(extract_links(archive='wikipedia_20190830.zip'))# that would take about 5 seconds on first run[Link(url='http://link0.org',text='text 0'),Link(url='http://link1.org',text='text 1'),Link(url='http://link2.org',text='text 2'),Link(url='http://link3.org',text='text 3'),Link(url='http://link4.org',text='text 4')]>>>fromtimeitimportTimer>>>res=Timer(lambda:list(extract_links(archive='wikipedia_20190830.zip'))).timeit(number=1)# second run is cached, so should take less time>>>print(f"took {int(res)} seconds to query cached items")took0secondstoquerycacheditems
工作原理
基本上,您的数据对象会变平 python类型映射到sqlite类型并返回
调用函数时,cachew
计算函数参数的哈希值
并将其与先前存储的哈希值进行比较。
如果它们匹配,它将反序列化并产生缓存数据库中存储的任何内容;如果哈希不匹配,则调用原始数据提供程序,并将新数据与新哈希一起存储。
功能
- 支持基本类型:
str
,int
,float
,bool
,datetime
,date
- 支持可选的
- 支持嵌套数据类型
- 支持返回类型推断:1,2
- 检测数据类型架构更改并自动丢弃旧数据
使用
有关参数和返回类型的最新文档,请参见docstring。 您还可以使用广泛的单元测试作为参考。
一些亮点:
缓存路径
可以是文件名,也可以指定一个可调用的返回路径并取决于函数的参数。不需要指定路径(它将在
/tmp
中创建),但建议使用。hashf
默认情况下,仅对所有参数进行哈希运算,您还可以指定一个自定义的可调用项。例如,它可用于放弃缓存输入文件已被修改。
cls
默认情况下是从返回类型注释推导出来的,但如果不控制要缓存的代码,则可以指定它。
安装
TODO
实施
为什么是元组和数据类?
元组在python中是很自然的,用于快速地将返回的结果分组在一起。
namedtuple
和dataclass
特别提供了一种非常简洁的、自文档化的方式来表示python中的一点数据。 非常紧凑的语法使得它非常方便,即使是在两个函数之间的一次性通信方式也是如此。如果您想了解更多为什么应该在代码中使用更多数据类,我建议您使用以下链接: 什么是数据类?,基本数据类
为什么不进行pickle?
对于普通数据类来说,pickling有点重。有很多报告称pickle甚至比json还要慢,这也有安全风险。最后,它只能通过python加载。
为什么要存储数据库?
它相当有效,而且命名的耦合序列以非常严格的方式映射到数据库行。
为什么不
pandas.dataframe
?数据帧很棒,可以序列化为csv或pickle。 作为与数据交互的一种方式,它们是很好的,但是由于它们的动态特性,抽象地考虑它很难方便。 它们也不能嵌套。
orms往往具有很强的攻击性,这可能会使脚本复杂化,甚至破坏性能。对于这样一个特定的目的来说,这也有点过分了。
- 例如,sqlalchemy需要使用自定义的sqlalchemy特定类型并继承基类。 它也不支持嵌套类型。
为什么不棉花糖?
marshmallow是将数据映射到数据库友好格式的常用方法,但它需要显式模式,如果已经以类型注释的形式使用了显式模式,则会造成开销。我已经查看了使用类型注释的现有项目,但没有发现它们涵盖了我想要的所有内容: