2012-05-31 39 views
10

我正在寻求关于在Python中实现对象持久性的方法的建议。更准确地说,我希望能够将Python对象链接到文件,以便任何打开该文件表示形式的Python进程都共享相同的信息,任何进程都可以更改其对象,并且这些更改会传播到其他进程,即使所有“存储”对象的进程都关闭,文件仍然会保留,并且可以由其他进程重新打开。我发现了三个主要的候选人 - 我的Python发行版 - anydbm,pickle和shelve(dbm看起来是完美的,但它只是Unix,而我在Windows上)。然而,他们都有缺陷:Python对象持久性

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

我的理想模块将表现为如下(以 “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的模块来实现这种行为,并编辑转储和加载行为,以便他们使用我上面提到的重复读取 - 但是我发现很难相信这个问题从未发生过,并且已经被修复,之前更有天赋的程序员。此外,对我来说,这些重复的读取看起来效率不高(尽管我必须承认我对操作复杂性的了解是有限的,而且这些重复的读取可能会在“幕后”进行,否则显然会更顺畅)。因此,我得出结论,我必须缺少一些能够为我解决问题的代码模块。如果有人能指出我的方向是正确的,或者对实施提出建议,我将不胜感激。

+4

给看看,以'蒙戈 - db'。它并没有像上面的例子那样完全集成到语言中,但它会为您提供一个比文件系统酸洗更为强大和容忍的数据库,并且对锁定很敏感。 – slezica

回答

11

改为使用ZODB(Zope对象数据库)。与ZEO支持它满足您的要求:

  • Python的透明持久性对象

    ZODB使用泡菜下方,所以,只要是泡菜,能够可以存储在ZODB对象存储。

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

    这意味着从一个进程更改传播到所有其他进程时,他们都准备好了,每个过程对整个数据的一致性视图交易。

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

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

一些ZODB存储是其他人添加功能的包装; DemoStorage例如保持内存中的变化,以促进单元测试和演示设置(重新启动,并且您再次拥有干净的平板)。 BeforeStorage为您提供时间窗口,仅在给定时间点返回之前的交易的数据。后者有助于为我恢复丢失的数据。

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

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

+0

ZODB听起来就像我想要的(RelStorage听起来像是一件有趣的事情,可以为将来查找) - 谢谢!我会测试它,并返回标记为答案,如果它适用于我。 – scubbo

+0

太棒了,谢谢你的建议! – scubbo

+0

这听起来像我想要的;在什么搁置提供。 – fatuhoku

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()