11

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

我在我的 Python 发行版中找到了三个主要的候选者——anydbm、pickle 和 shelve(dbm 看起来很完美,但它只适用于 Unix,而且我在 Windows 上)。但是,它们都有缺陷:

  • anydbm 只能处理字符串值的字典(我正在寻求存储一个字典列表,所有这些字典都有字符串键和字符串值,但理想情况下我会寻找一个没有类型限制的模块)
  • 搁置要求在更改传播之前重新打开文件 - 例如,如果两个进程 A 和 B 加载相同的文件(包含搁置的空列表),并且 A 将项目添加到列表并调用 sync(),B 将在重新加载文件之前,列表仍然是空的。
  • pickle(我目前用于我的测试实现的模块)具有与搁置相同的“重新加载要求”,并且也不会覆盖以前的数据 - 如果进程 A 将 15 个空字符串转储到文件中,然后是字符串 'hello',进程 B 必须加载文件 16 次才能获得“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)中进行)。因此,我得出结论,我必须缺少一些可以为我解决问题的代码模块。如果有人能指出正确的方向,或者就实施提供建议,我将不胜感激。

4

3 回答 3

11

请改用ZODB(Zope 对象数据库)。在 ZEO 的支持下,它可以满足您的要求:

  • Python 对象的透明持久性

    ZODB 在下面使用泡菜,因此任何可以泡菜的东西都可以存储在 ZODB 对象存储中。

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

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

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

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

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

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

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

于 2012-05-31T09:54:29.483 回答
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()
于 2014-05-15T04:39:50.180 回答
0

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

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

于 2020-10-27T22:08:03.553 回答