Альтернативы `persistent_id` от pickle?

#python #json #persistence #yaml #pickle

#python #json #постоянство #yaml #pickle

Вопрос:

Я использую модуль pickle от Python для реализации тонкого слоя сохранения на основе файлов. Уровень сохранения (часть более крупной библиотеки) в значительной степени зависит от функции persistent_id от pickle для сохранения объектов указанных классов в виде отдельных файлов.

Единственная проблема с этим подходом заключается в том, что файлы pickle недоступны для редактирования человеком, и я бы предпочел, чтобы объекты сохранялись в формате, удобочитаемом и редактируемом с помощью текстового редактора (например, YAML или JSON).

Знаете ли вы какую-либо библиотеку, которая использует формат, доступный для редактирования человеком, и предлагает функции, аналогичные pickle persistent_id ? В качестве альтернативы, есть ли у вас предложения по их реализации поверх библиотеки сериализации на основе YAML или JSON, без перезаписи большого подмножества pickle?

Комментарии:

1. Трудно вносить предложения о том, как реализовать систему сохранения без какого-либо описания ее назначения или требований.

2. @taleinat что ж, вопрос имеет более узкую область применения, чем реализация системы сохранения: есть ли какая-либо библиотека, которая предлагает функцию, аналогичную механизму pickle persistent_id , но использует формат, доступный для редактирования человеком? (Но вы правы, последняя часть может вызвать некоторую путаницу — я попытаюсь ее перефразировать)

Ответ №1:

Я сам еще не пробовал это, но я думаю, вы сможете сделать это элегантно с помощью 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)
 

Отныне используйте my_yaml_dump вместо yaml.dump , когда вы хотите сохранить экземпляры Persistable класса в отдельные файлы. Но не используйте его внутри persistable_representer и persistable_constructor ! Никакой специальной функции загрузки не требуется, просто используйте yaml.load .

Фух, это потребовало некоторой работы… Надеюсь, это поможет!

Комментарии:

1. Большое спасибо за предложение! Однако я не могу заставить это работать (по крайней мере, нелегко): gist.github.com/1348843 Основные проблемы: (1) PyYAML решает, какой представитель использовать, основываясь только на основном классе объекта, поэтому добавление атрибута persistent_id или создание подклассов объектов указанного Persistent класса не работает; (2) Поэтому представитель функция не может решить, использовать ли представление «cross-ref» или простое YAML: для этого потребуется указать, когда представитель вызывается как часть сброса другого объекта, или «изначально» — в противном случае мы получаем бесконечную рекурсию.

2. Ваш код вводит бесконечную рекурсию, потому что вы вызываете yaml.dump(data) внутри представителя, и вы сказали yaml.dump использовать этот представитель для сброса этих данных! Попробуйте использовать пример представителя / конструктора на странице документации PyYAML, на которую я ссылался. В частности, вы должны использовать dumper.represent_scalar вместо yaml.dump внутри вашего представителя и loader.construct_scalar вместо yaml.load в вашем конструкторе.

3. Да, я знаю, почему он входит в бесконечную рекурсию. Дело в том, что я не могу использовать функции represent_scalar / construct_scalar , потому что «постоянные» объекты не являются простыми скалярами, это объекты Python, из которых я должен получить «обычное» представление YAML. Это то, что persistent_id делает pickle: он генерирует обычное маринованное представление, но сохраняет его в другой файл.

4. Я вижу, вы хотите снова получить yaml.dump объект, но без специальной обработки, и сохранить его в файл, правильно? Если да, то не будет ли делать то же самое с pickle той же проблемой? Как вы преодолели это в своем решении на основе pickle?

5. Да, правильно. В решении на основе pickle я не делал никакой особой магии; это особенность pickle, описанная здесь .