Python对象持久性

时间:2012-05-31 09:28:23

标签: python persistence

我正在寻求有关在Python中实现对象持久性的方法的建议。更确切地说,我希望能够将Python对象链接到文件,使得打开该文件表示的任何Python进程共享相同的信息,任何进程都可以更改其对象,并且更改将传播到其他进程,即使关闭“存储”对象的所有进程,该文件仍将保留,并可由另一个进程重新打开。

我在发布Python时找到了三个主要的候选者 - anydbm,pickle和shelve(dbm似乎很完美,但它只支持Unix,我在Windows上)。但是,它们都有缺陷:

  • anydbm只能处理字符串值的字典(我正在寻找存储字典列表,所有字典都有字符串键和字符串值,但理想情况下我会寻找一个没有类型限制的模块)
  • shelve要求在更改传播之前重新打开文件 - 例如,如果两个进程A和B加载相同的文件(包含搁置的空列表),并且A将一个项添加到列表并调用sync()在重新加载文件之前,B仍然会将列表视为空。
  • pickle(我目前用于我的测试实现的模块)具有与shelve相同的“重新加载要求”,并且也不会覆盖以前的数据 - 如果进程A将十五个空字符串转储到一个文件上,然后是字符串'你好',进程B必须加载文件十六次才能获得'hello'字符串。我目前正在处理这个问题,通过重复读取的任何写操作,直到文件结束(“在写入之前擦除平板”),并使每次读取操作重复直到文件结束,但我觉得必须有更好的方式。

我的理想模块的行为如下(“A>>>”表示由流程A执行的代码,“B>>>”代码由流程B执行):

A>>> import imaginary_perfect_module as mod
B>>> import imaginary_perfect_module as mod
A>>> d = mod.load('a_file') 
B>>> d = mod.load('a_file')
A>>> d
{}
B>>> d
{}
A>>> d[1] = 'this string is one'
A>>> d['ones'] = 1   #anydbm would sulk here
A>>> d['ones'] = 11 
A>>> d['a dict'] = {'this dictionary' : 'is arbitrary', 42 : 'the answer'}
B>>> d['ones']   #shelve would raise a KeyError here, unless A had called d.sync() and B had reloaded d
11    #pickle (with different syntax) would have returned 1 here, and then 11 on next call
(etc. for B)

我可以通过创建自己的模块来实现这种行为,该模块使用pickle,并编辑转储和加载行为,以便它们使用我上面提到的重复读取 - 但我发现很难相信这个问题从未发生过,并且之前被更多有才华的程序员修复过。而且,这些重复读取对我来说似乎效率低下(虽然我必须承认我对操作复杂性的了解有限,并且这些重复读取可能会在“幕后”进行,否则显然更平滑的模块如shelve)。因此,我得出结论,我必须缺少一些可以解决问题的代码模块。如果有人能指出我正确的方向,或者提出有关实施的建议,我将不胜感激。

3 个答案:

答案 0 :(得分:11)

请改用ZODB(Zope对象数据库)。以ZEO为后盾,满足您的要求:

  • Python对象的透明持久性

    ZODB使用下面的泡菜,所以任何可挑剔的东西都可以存储在ZODB对象库中。

  • 完全与ACID兼容的事务支持(包括保存点)

    这意味着当一个流程良好且准备就绪时,一个流程的更改会传播到所有其他流程,并且每个流程在整个事务中对数据都有一致的视图。

ZODB已经存在了十多年了,所以你猜对了这个问题已经解决了。 : - )

ZODB让你插上存储空间;最常见的格式是FileStorage,它将所有内容存储在一个Data.fs中,并为大对象提供可选的blob存储。

一些ZODB存储是围绕其他存储的包装器以添加功能;例如,DemoStorage会保留内存中的更改,以便于进行单元测试和演示设置(重新启动并再次获得清理)。 BeforeStorage为您提供了一个及时窗口,仅在给定时间点之前从事务返回数据。后者有助于为我恢复丢失的数据。

ZEO是一个引入客户端 - 服务器架构的插件。使用ZEO可以让您一次从多个进程访问给定的存储;如果您只需要从一个进程进行多线程访问,则不需要此层。

使用RelStorage可以实现同样的目的,它将ZODB数据存储在关系数据库中,如PostgreSQL,MySQL或Oracle。

答案 1 :(得分:2)

对于初学者,您可以将搁置数据库移植到ZODB数据库,如下所示:

#!/usr/bin/env python
import shelve
import ZODB, ZODB.FileStorage
import transaction
from optparse import OptionParser
import os
import sys
import re

reload(sys)
sys.setdefaultencoding("utf-8")

parser = OptionParser()

parser.add_option("-o", "--output", dest = "out_file", default = False, help ="original shelve database filename")
parser.add_option("-i", "--input", dest = "in_file", default = False, help ="new zodb database filename")

parser.set_defaults()
options, args = parser.parse_args()

if options.in_file == False or options.out_file == False :
    print "Need input and output database filenames"
    exit(1)

db = shelve.open(options.in_file, writeback=True)
zstorage = ZODB.FileStorage.FileStorage(options.out_file)
zdb = ZODB.DB(zstorage)
zconnection = zdb.open()
newdb = zconnection.root()

for key, value in db.iteritems() :
    print "Copying key: " + str(key)
    newdb[key] = value

transaction.commit()

答案 2 :(得分:0)

我建议使用TinyDB,它使用起来更好,更简单。

https://tinydb.readthedocs.io/en/stable/