`persistent_id` 的替代方案?

5 投票
1 回答
832 浏览
提问于 2025-04-17 05:14

我一直在用Python的pickle模块来实现一个简单的文件存储功能。这个存储功能是一个更大库的一部分,主要依赖于pickle的persistent_id特性,把特定类的对象保存为单独的文件。

不过,这种方法有个问题,就是pickle文件不容易被人直接编辑。我更希望能把对象保存成一种人类可读且可以用文本编辑器编辑的格式,比如YAML或JSON。

你知道有没有什么库可以使用人类可编辑的格式,并且提供类似于picklepersistent_id功能吗?或者,你有没有建议可以在基于YAML或JSON的序列化库上实现这些功能,而不需要重写pickle的大部分内容?

1 个回答

4

我自己还没试过这个,不过我觉得你可以用PyYAML来优雅地实现这个功能,他们称之为“表示器”和“解析器”

编辑

在和提问者进行了一番详细的评论交流后,这里有一种方法可以用PyYAML实现所需的功能。

重要提示:如果一个Persistable实例有另一个这样的实例作为属性,或者以某种方式包含在它的某个属性里,那么这个被包含的Persistable实例不会被保存到另一个单独的文件里,而是会和父Persistable实例一起保存在同一个文件中。根据我的理解,这个限制在提问者的基于pickle的系统中也存在,可能对于他的使用场景是可以接受的。我还没有找到一个优雅的解决方案来处理这个问题,而不需要修改yaml.representer.BaseRepresenter

import yaml
from functools import partial

class Persistable(object):
    # simulate a unique id
    _unique = 0

    def __init__(self, *args, **kw):
        Persistable._unique += 1
        self.persistent_id = ("%s.%d" %
                              (self.__class__.__name__, Persistable._unique))

def persistable_representer(dumper, data):
    id = data.persistent_id
    print "Writing to file: %s" % id
    outfile = open(id, 'w')
    outfile.write(yaml.dump(data))
    outfile.close()
    return dumper.represent_scalar(u'!xref', u'%s' % id)

class PersistingDumper(yaml.Dumper):
    pass

PersistingDumper.add_representer(Persistable, persistable_representer)
my_yaml_dump = partial(yaml.dump, Dumper=PersistingDumper)

def persistable_constructor(loader, node):
    xref = loader.construct_scalar(node)
    print "Reading from file: %s" % id
    infile = open(xref, 'r')
    value = yaml.load(infile.read())
    infile.close()
    return value

yaml.add_constructor(u'!xref', persistable_constructor)


# example use, also serves as a test
class Foo(Persistable):
    def __init__(self):
        self.one = 1
        Persistable.__init__(self)

class Bar(Persistable):
    def __init__(self, foo):
        self.foo = foo
        Persistable.__init__(self)

foo = Foo()
bar = Bar(foo)
print "=== foo ==="
dumped_foo = my_yaml_dump(foo)
print dumped_foo
print yaml.load(dumped_foo)
print yaml.load(dumped_foo).one

print "=== bar ==="
dumped_bar = my_yaml_dump(bar)
print dumped_bar
print yaml.load(dumped_bar)
print yaml.load(dumped_bar).foo
print yaml.load(dumped_bar).foo.one

baz = Bar(Persistable())
print "=== baz ==="
dumped_baz = my_yaml_dump(baz)
print dumped_baz
print yaml.load(dumped_baz)

从现在开始,当你想把Persistable类的实例保存到单独的文件时,使用my_yaml_dump而不是yaml.dump。但是不要persistable_representerpersistable_constructor里面使用它!不需要特殊的加载函数,只需使用yaml.load

呼,这可花了我不少功夫……希望这能帮到你!

撰写回答